HOJ 魔法少女Clara的快乐训练(名字是自己编的,因为看不到原题的名字QWQ) dp
题意概述:
多组测试。Clara在N天中至多选择M天进行训练,每天题目的难度为ci,Clara初始实力值为0。现在假如第i天正常训练,那么假设当前实力值为x,当天题目难度为y。
当y<x-100,Clara实力值变为0(摸鱼)
当x-100<=y<=x,Clara实力值加一(锻炼了手速)
当x<y<=x+100,Clara实力值增加(y-x+1)/2,向下取整(充分的锻炼)
当y>x+100,Clara实力值增加5000/(y-x),向下取整(成功或者失败地突破了极限)
现在大仙Claris被Clara感动了,决定可以在N天中选择连续的3天对Clara进行特训(这三天算在M天中),若特训前实力值不超过2148473547,则特训后实力值增加100,否则实力值增加99。
问N天后Clara实力值最大是多少。
数据范围:最多10组数据,1 <= m <= n <= 3000,0 <= Ci <= 1e8,Time limit : 5 s,Memory limit : 512 mb
分析:
想想我开始强行写BFS爆搜还剪枝的操作,挺迷的……因为这个题就长了一张dp的脸啊QWQ,一开始没有拨开题目的包装导致没有找到最优子结构的形式,以为不能dp。
首先分析实力值的变化,把不等式变形一下,变成x>y+100, y<=x<=y+100, y-100<=x<y, x<y-100。这样看来,比较舒服(变化的应该是x,所以把x写在由y确定的区间里面)。然后分析一下这几个不等式的几句话,有几个重要的信息藏在里面:
1.最优方案中,摸鱼导致实力值变成0的情况不能发生,突破极限失败的情况也不可以发生,也就是说实力值应当是单调递增的。
2.实力值每次最多增加50,那么N天后实力值最大为150000,显然每次Claris给Clara特训的时候实力值都是增加100。
3.若x<y-100,那么训练后x落在[x,x+49],即[x,y-52]
若y-100<=x<y,那么训练后x落在[y-50,y]
若y<=x<=y+100,那么训练后x落在[y+1,y+101]
也就是说,假如当前实力值x在第i天不会摸鱼,那么实力值x越大,第i天训练后实力值越大。
4.假设前i天最大实力值是x,对于第j天 ( j>i ) ,x>y+100,那么任何第j天进行训练的方案训练后的实力值都不可能比x大。
根据上面的分析,已经可以构思dp方程了。
定义:设f(n,m,0/1)表示前n天,至少训练m天,是否有进行过特殊训练,的最大实力值。
转移:考虑f(n,m,0)。第n天不训练,则从f(n-1,m,0)转移。
第n天训练(伪),那么若f(n-1,m-1,0)>c[n]+100,从f(n-1,m-1,0)直接转移;否则对实力值f(n-1,m-1,0)在第n天进行训练,转移。
考虑f(n,m,1)。第n天不训练和一般训练同上,对于特殊训练,保证下标有意义的情况下转移即可。
答案:MAX( f(N,M,0) , f(N,M,1) )
边界:f(0,0,0)=0,注意f中第一维时刻不小于第二维(这也是粗体字下划线部分必要的原因)
时间复杂度:O(NM)
AC代码:
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdbool.h> 4 #define maxn 3005 5 #define inf 1e8 6 7 int T,N,M,C[maxn]; 8 int f[maxn][maxn][2]; 9 10 int Min(int x,int y){ return x<y?x:y; } 11 int Max(int x,int y){ return x>y?x:y; } 12 13 void data_in() 14 { 15 scanf("%d%d",&N,&M); 16 for(int i=1;i<=N;i++) 17 scanf("%d",&C[i]); 18 } 19 int g(int x,int n) 20 { 21 if(x-100<=C[n]&&C[n]<=x) return x+1; 22 if(x<C[n]&&C[n]<=x+100) return x+(C[n]-x+1)/2; 23 if(C[n]>x+100) return x+5000/(C[n]-x); 24 return 0; 25 } 26 void dp() 27 { 28 memset(f,0,sizeof(f)); 29 for(int i=1;i<=N;i++){ 30 int U=Min(i,M); 31 for(int j=1;j<=U;j++){ 32 int tmp=-inf; 33 if(i-1>=j) tmp=f[i-1][j][0]; 34 if(f[i-1][j-1][0]>C[i]+100) tmp=Max(tmp,f[i-1][j-1][0]); 35 else tmp=Max(tmp,g(f[i-1][j-1][0],i)); 36 f[i][j][0]=tmp; 37 38 tmp=-inf; 39 if(i-1>=j) tmp=f[i-1][j][1]; 40 if(f[i-1][j-1][1]>C[i]+100) tmp=Max(tmp,f[i-1][j-1][1]); 41 else tmp=Max(tmp,g(f[i-1][j-1][1],i)); 42 if(i>=3&&j>=3) tmp=Max(tmp,f[i-3][j-3][0]+100); 43 f[i][j][1]=tmp; 44 } 45 } 46 } 47 void work() 48 { 49 dp(); 50 int ans=Max(f[N][M][0],f[N][M][1]); 51 printf("%d\n",ans); 52 } 53 int main() 54 { 55 freopen("test.in","r",stdin); 56 freopen("test.out","w",stdout); 57 scanf("%d",&T); 58 for(int t=1;t<=T;t++){ 59 printf("Case #%d: ",t); 60 data_in(); 61 work(); 62 } 63 return 0; 64 }

浙公网安备 33010602011771号