题意概述:
多组测试。Clara在N天中至多选择M天进行训练,每天题目的难度为ci,Clara初始实力值为0。现在假如第i天正常训练,那么假设当前实力值为x,当天题目难度为y。
当y
当x-100<=y<=x,Clara实力值加一(锻炼了手速)
当x
当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变化的应该是x,所以把x写在由y确定的区间里面)。然后分析一下这几个不等式的几句话,有几个重要的信息藏在里面:
1.最优方案中,摸鱼导致实力值变成0的情况不能发生,突破极限失败的情况也不可以发生,也就是说实力值应当是单调递增的。
2.实力值每次最多增加50,那么N天后实力值最大为150000,显然每次Claris给Clara特训的时候实力值都是增加100。
3.若x
若y-100<=x
若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
2 #include<string.h>
3 #include
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 xx: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(x100) 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 }
View Code