Sleeping
题目链接:Click Here~
题目分析:
又是一道DP题,每次都是一眼看穿,每次都是不会正确推出状态转移方程式。悲剧。。~-~
说有一个搞ACM的人,天天逃课搞ACM。但是快到期末了,他为了学分只能去上课了。由于他是熬夜刷题,所以他总是在课堂上睡觉(orz)。为了学分,他决定不能继续睡下去了。于是他制定了一个计划,就是在一堂课的n分钟中连续的听老师讲课不小于L分钟,但是每堂课睡觉时间不能少于m分钟(毕竟不是神啊,还是需睡觉地-_-)。而且每堂课每分钟都有一个学分值,现在要你求出在满足了上面的要求下如何得到最大的学分。学分是主要啊!这个大家都懂的。
算法分析:
我个人感觉跟那个啥,跟HDU的Sum plus plus有点像。就是要运用到相同的思想,但是差别还是有点的,就是Sum plus plus更难一点。说说这题如何推状态转移方程式吧。
我们用dp[i][j]表示前i分钟睡觉用了j分钟的最大学分值。
=====>>则可以推出一个状态:dp[i][j] = max(dp[i-1][j-1],dp[i-1][j]+a[i]);
这个的意思是在第i分钟是睡还是不睡,去其中的最大值。而我们知道其中不睡还有分两种情况,这就是这道题的精髓所在,也就是为什么我说跟Sum plus plus有点像的原因。
第一种:把第i分钟不睡的值归入到前面的不睡的时间中。(在i分钟里连续听课的时间不一定大于L)
第二种:以第i分钟结尾,把前i-L作为开始听课的起点。且我们必须保证前提i-L>=j(因为至少要保证有前i分钟又j分钟的睡觉时间啊!)。
为什么要这么麻烦的分两种情况呢?你如果认真思考过,应该就会知道了。如果,直接求得话时间复杂度是O(N^3)。
所以,最后我们会得到最后的状态是:
d[i][j] = max{d[i-1][j]+a[i](接着前面不睡的时间),dp[i-L][j]+sum[i]-sum[i-L](以i-L作为不睡的起点)}。
最后吐槽一下HDU的判题系统,我看到有的人写的程序都超数组下界了还可以AC。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1e3 + 5; int dp[maxn][maxn],d[maxn][maxn]; int sum[maxn],a[maxn]; int n,L,m; int Solve() { memset(dp,0,sizeof(dp)); memset(d,0,sizeof(d)); for(int i = 1;i <= n;++i){ for(int j = 0;j<=i&&j <= m;++j){ if(i-ll >= j) d[i][j] = max(d[i-1][j]+a[i],dp[i-ll][j]+sum[i]-sum[i-ll]); //不睡的最大收益 if(j>=1) //注意这里!! dp[i][j] = max(d[i][j],dp[i-1][j-1]); else dp[i][j] = d[i][j]; // printf("j-1---> %d\n",j-1); } } return dp[n][m]; } int main() { while(~scanf("%d%d%d",&n,&m,&L)) { sum[0] = 0; for(int i = 1;i <= n;++i){ scanf("%d",&a[i]); sum[i] = sum[i-1]+a[i]; } printf("%d\n",Solve()); } return 0; }
HDU3905 Sleeping,布布扣,bubuko.com
原文:http://blog.csdn.net/zhongshijunacm/article/details/22689669