您的位置:首页 > 产品设计 > UI/UE

UESTC - 594 我要长高(DP+滚动数组优化+单调队列优化)

2018-01-07 21:37 288 查看
    韩父有NN个儿子,分别是韩一,韩二…韩NN。由于韩家演技功底深厚,加上他们间的密切配合,演出获得了巨大成功,票房甚至高达20002000万。舟子是名很有威望的公知,可是他表面上两袖清风实则内心阴暗,看到韩家红红火火,嫉妒心遂起,便发微薄调侃韩二们站成一列时身高参差不齐。由于舟子的影响力,随口一句便会造成韩家的巨大损失,具体亏损是这样计算的,韩一,韩二…韩NN站成一排,损失即为C×C×(韩ii与韩i+1i+1的高度差(1≤i<N1≤i<N))之和,搞不好连女儿都赔了.韩父苦苦思索,决定给韩子们内增高(注意韩子们变矮是不科学的只能增高或什么也不做),增高11cm是很容易的,可是增高1010cm花费就很大了,对任意韩ii,增高HHcm的花费是H2H2.请你帮助韩父让韩家损失最小。

      题意很清楚,但是一上来做的话很容易没有思路,但是如果知道这是一道dp题之后就好入手了很多。很显然的,我们从韩1到韩n计算,韩i子的情况一定与韩i-1子的状况有关,递归关系很明显,这里用dp【i】【j】表示韩i子的身高为j时的结果,可以写出dp公式:dp[i][j]=Min(dp[i][j], (dp[i-1][k]+Abs(j-k)*C+(h[i]-j)*(h[i]-j)));  这里dp【i-1】【k】表示韩i-1子身高为k时的结果(已知),h【i】存的是韩i的身高。看一下这个式子里的未知量:i、j、k,需要三重循环O(n*100*100),超时。但是我还是把它写了下来,因为单论这一部分的dp而言就够一个题了,再加上后面会提到的单调队列优化的话,我一上来实在是看不懂,跨度太大跳不过去,只能自己按部就班慢慢优化出来。另外,我觉得那个100可以稍微优化上一点点,也就是上下限换成h【】的最小最大值,说不定能过的,然而并没有。

      对于找出来的dp公式先去掉Abs()分解开:

        1、韩i比韩i-1高时(j>k)   dp[i][j]=Min(dp[i][j], dp[i-1][k]-k*C+j*C+X);

        2、韩i比韩i-1矮时(j<k)   dp[i][j]=Min(dp[i][j], dp[i-1][k]+k*C-j*C+X);   (X=(h[i]-j)*(h[i]-j))

  然后就是滚动数组+单调队列优化dp了。先单独说下知识点的问题:滚动数组,并不算陌生,之前用过一两次,前天我再看LCS的时候也见到过滚动数组的使用;滚动数组并不算是一个知识点,而是一个工具,优化空间的工具。做dp的的话,方程难找是另一个层次的问题了,可在有了方程的情况下,数组开不出来实在是很难受的,这时候往往就要滚动数组了。这个题中n*100的数组是能开的,但是看的题解上直接就是最优方法,也就学了过来。单调队列优化dp,单调队列是专门练过的,比较熟悉,但是想到优化dp我是很懵逼的,做完了这题后也算是初步了解;就这个题而言,对于韩i子,我们需要的是(1...100)枚举韩i的身高j,然后里面套一层(1...100)枚举韩i-1的身高k并从中找出与j相配的最合适的k(也就是dp[i-1][k]±k*C最小值),而我们观察上面拆分了的两个式子,每一种都情况中的j,所要匹配的k实际上不是全部(1...100),而只是其中的一部分,而两者恰好可以用一个单调队列来维护,具体在代码中体现。两个,分别是二维数组和滚动数组(也是二维,不过只有2,相当于一维了pre[]+now[])两种方案。还有更加精简的,直接用一个最小值minn维护就可以了,不再写了,稍微改动即可。

超时代码如下:

#include<stdio.h>
const int MAX=50010;
const int INF=1e9;
int N, C;
int h[MAX];
int dp[MAX][110];//韩i身高为j时
int Abs(int x)
{
if(x>0)
return x;
return -x;
}
int Min(int x, int y)
{
if(x>y)
return y;
return x;
}
int main()
{
while(scanf("%d%d", &N, &C))
{
int left=1e9, right=0;
for(int i=1; i<=N; i++)
{
scanf("%d", &h[i]);
if(left>h[i])
left=h[i];
if(right<h[i])
right=h[i];
}
for(int i=0; i<=N; i++)//初始化为极大值
{
for(int j=0; j<=100; j++)
{
dp[i][j]=INF;
}
}
for(int i=left; i<=right; i++)//初始化dp[0][]
dp[0][i]=0;
for(int i=1; i<=N; i++)//韩i
{
for(int j=h[i]; j<=right; j++)//韩i的身高
{
for(int k=h[i-1]; k<=right; k++)//韩i-1的身高
{
dp[i][j]=Min(dp[i][j], (dp[i-1][k]+Abs(j-k)*C+(h[i]-j)*(h[i]-j)));
//动规 公式
}
}
}
int ans=dp
[right];
for(int i=left; i<=right; i++)
{
if(ans>dp
[i])
ans=dp
[i];
}
printf("%d\n", ans);
}
return 0;
}


AC代码如下:(二维数组+单调队列优化)

#include<cstdio>
using namespace std;
const int MAX=50010;
const int INF=1e9;
int N, C;
int dp[MAX][110];//韩i身高为j时
int q[MAX];
int Min(int x, int y)
{
if(x<y)
return x;
return y;
}
int main()
{
while(~scanf("%d%d", &N, &C) && (N+C))
{
int head=0, tail=0;
int x;
scanf("%d", &x);
for(int i=0; i<=100; i++)//对韩1 初始化
{
if(i<x)
dp[1][i]=INF;
else
dp[1][i]=(i-x)*(i-x);
}
for(int i=2; i<=N; i++)//韩2——》韩n
{
scanf("%d", &x);
head=tail=0;
for(int j=0; j<=100; j++)//韩i比韩i-1高时
{
int temp=dp[i-1][j]-j*C;//韩i-1的半个状态
while(head<tail && temp<q[tail-1])
tail--;
q[tail++]=temp;
if(j>=x)//求韩i身高为j时
dp[i][j]=q[head]+j*C+(x-j)*(x-j);
else
dp[i][j]=INF;
}

head=tail=0;
for(int j=100; j>=0; j--)//韩i比韩i-1矮时
{
int temp=dp[i-1][j]+j*C;
while(head<tail && temp<q[tail-1])
tail--;
q[tail++]=temp;
if(j>=x)//求韩身高为j时
dp[i][j]=Min(dp[i][j], q[head]-j*C+(x-j)*(x-j));
else
dp[i][j]=INF;
}
}
int ans=INF;
for(int i=0; i<=100; i++)
{
ans=Min(ans, dp
[i]);
}
printf("%d\n", ans);
}
return 0;
}


AC代码2(滚动数组+单调队列优化)

#include<cstdio>
using namespace std;
const int MAX=50010;
const int INF=1e9;
int N, C;
int dp[2][110];//韩i身高为j时
int q[MAX];
int Min(int x, int y)
{
if(x<y)
return x;
return y;
}
int main()
{
while(~scanf("%d%d", &N, &C) && (N+C))
{
int head=0, tail=0;
int x;
scanf("%d", &x);
int now=1;
for(int i=0; i<=100; i++)//对韩1 初始化
{
if(i<x)
dp[now][i]=INF;
else
dp[now][i]=(i-x)*(i-x);
}
for(int i=2; i<=N; i++)//韩2——》韩n
{
scanf("%d", &x);
head=tail=0;
now=now^1;//now=now%2;  now=1-now;  均可
for(int j=0; j<=100; j++)//韩i比韩i-1高时
{
int temp=dp[now^1][j]-j*C;//韩i-1的半个状态
while(head<tail && temp<q[tail-1])
tail--;
q[tail++]=temp;
if(j>=x)//求韩i身高为j时
dp[now][j]=q[head]+j*C+(x-j)*(x-j);
else
dp[now][j]=INF;
}

head=tail=0;
for(int j=100; j>=0; j--)//韩i比韩i-1矮时
{
int temp=dp[now^1][j]+j*C;
while(head<tail && temp<q[tail-1])
tail--;
q[tail++]=temp;
if(j>=x)//求韩身高为j时
dp[now][j]=Min(dp[now][j], q[head]-j*C+(x-j)*(x-j));
else
dp[now][j]=INF;
}
}
int ans=INF;
for(int i=0; i<=100; i++)
{
ans=Min(ans, dp[now][i]);
}
printf("%d\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: