PROBLEM3 睡觉
问题描述:
为了提高程序解题能力,勤奋努力的QQ天天锯题到深夜,导致睡眠严重不足,可NOIP决赛就要来临了,必须要有良好的状态才行啊,因此QQ决定准备拿出一天时间,好好补补觉。
他把这一天等分成了n个时间段,在每个时间段睡觉能获得精神点数不尽相同,在第i段时间能获得V[i]的精神点数。
由于勤奋的QQ觉得整天都睡太堕落了,他决定最多只能睡m个时间段。至于其他的时间吗...那自然是勤奋地锯题...
有两点事情要特别提出注意:
1.QQ不可能一上床马上睡着,他在连续一段睡觉时间的第一个时间段不能获得此时的V。也就是说如果他在i...j中的所有时间都休息了,获得的精神点数为V[i+1]+...+V[j]。
2.所有的时间段呈环形分布,也就是说第n个时间段之后为第1个时间段。
要求的自然是QQ最多能获得的精神点数之和。
输入格式:
第一行两个正整数n,m,意义如题所述
接下来n行,每行一个非负整数V[i]
输出格式:
一行,表示QQ最多能获得的精神点数之和
输入样例:
5 3
2
0
3
1
4
输出样例:
6
样例解释:
选择4、5、1三个时间段休息,在5、1时睡着,最大值为4+2=6
数据范围:
对于20%的数据n<=20
对于50%的数据n<=200
对于100%的数据n<=5000,m<=n,V[i]<=10000
这是一道动规,考试的时候50分,其他五组超时,原因是因为我看到是有环,就用了加倍,枚举起始点的方法,把时间复杂的增加了一维。
事实上,这道题环的表现不明显。可以只用一条链多一点的方法就可以了。
首先考虑一条链上。当前状态与总共睡了多长时间、现在的时刻、上一状态睡没有有关,因此状态是三维。
f[i][j][0~1]表示前i分钟,总共睡j分钟,上一分钟有没有在睡。
转移即:f[i][j][0] = max{f[i-1][j][0],f[i-1][j][1]}。f[i][j][1] = max{f[i-1][j-1][0],f[i-1][j-1][1]+v[i]}。
考虑枚举起点,对n条链进行DP,因为每一条链是独立的,因此每次DP完都必须给下次的DP赋初值(考试时这里调试了很久,至少半个小时)
(考虑一个反例,1时刻睡了,推出n时刻睡了的最优解。第二条链,n时刻睡了的最优解推出1时刻睡了的最优解。1时刻多睡了一次)
我就是不知如何处理这种情况,因此退了一步,进行了n次DP,超时了。
最优方法是,首先用普通方法以1为起点进行一次DP,求出一个解。第二步,1必须睡,以2为起点,n必须睡,求一个最优解。
第二种情况,即为从后面的一个时刻一直睡到1时刻,必须单独讨论。
当前状态只与上一阶段状态有关,可以进行01滚动。01滚动用了陈样说那个优化,把一个二维数组拆成多个,减少了乘法运算,不知道有没有用。
50分程序
后来发现有点错误,不知道为什么还能对。。就是f1[j][0]和f0[j][0]应该赋值为0,而不是-∞,因为他不是无效状态,
单步调试的时候,发现很多有效状态不见了,原来是这个原因。已经更正。
//#include <iostream>
//using std::cout;
//using std::cin;
#include <cstdio>
const long oo = 0x7fff0000;
long n;long m;
long f0[10010][2];
long f1[10010][2];
long v[10010];
int main()
{
freopen("sleep.in","r",stdin);
freopen("sleep.out","w",stdout);
scanf("%ld%ld",&n,&m);
for (long i=1;i<n+1;i++)
{
scanf("%ld",v+i);
v[i+n] = v[i];
}
v[2*n+1] = v[1];
v[2*n+2] = v[2];
long ans = 0;
for (long l=1;l<n+1;l++)
{
if ((l&1)==1)
{
f0[0][0]=0;
for (long j=0;j<m+1;j++)
{
f0[j][0]=0;
f0[j][1]=-oo;
f1[j][1]=-oo;
f1[j][0]=0;
}
}
else
{
f1[0][0]=0;
for (long j=0;j<m+1;j++)
{
f1[j][0]=0;
f1[j][1]=-oo;
f0[j][1]=-oo;
f0[j][0]=0;
}
}
for (long i=l;i<l+n;i++)
{
if ((i&1)==1)
{
for (long j=1;j<m+1;j++)
{
if (j-1<=i-1-l+1)
{
if (f1[j][1]<f0[j-1][0])
f1[j][1]=f0[j-1][0];
if (f1[j][1]<f0[j-1][1]+v[i])
f1[j][1]=f0[j-1][1]+v[i];
}
if (f1[j][0]<f0[j][1])
f1[j][0]=f0[j][1];
if (f1[j][0]<f0[j][0])
f1[j][0]=f0[j][0];
}
for (long j=0;j<m+1;j++)
{
f0[j][1]=-oo;
f0[j][0]=0;
}
}
else
{
for (long j=1;j<m+1;j++)
{
if (j-1<=i-1-l+1)
{
if (f0[j][1]<f1[j-1][0])
f0[j][1]=f1[j-1][0];
if (f0[j][1]<f1[j-1][1]+v[i])
f0[j][1]=f1[j-1][1]+v[i];
}
if (f0[j][0]<f1[j][1])
f0[j][0]=f1[j][1];
if (f0[j][0]<f1[j][0])
f0[j][0]=f1[j][0];
}
for (long j=0;j<m+1;j++)
{
f1[j][1]=-oo;
f1[j][0]=0;
}
}
}
if ( ((l+n-1)&1) ==1)
{
if (ans<f1[m][0])
ans = f1[m][0];
if (ans<f1[m][1])
ans = f1[m][1];
}
else
{
if (ans<f0[m][0])
ans = f0[m][0];
if (ans<f0[m][1])
ans = f0[m][1];
}
}
printf("%ld",ans);
return 0;
}
AC程序
//#include <iostream>
//using std::cout;
//using std::cin;
#include <cstdio>
const long oo = 0x7fff0000;
long n;long m;
long f0[5010][2];
long f1[5010][2];
long v[5010];
int main()
{
freopen("sleep.in","r",stdin);
freopen("sleep.out","w",stdout);
scanf("%ld%ld",&n,&m);
for (long i=1;i<n+1;i++)
{
scanf("%ld",v+i);
}
v[n+1] = v[1];
v[n+2] = v[2];
if (n==m)
{
long min = oo;
long sum = 0;
for (long i=1;i<n+1;i++)
{
min <?= v[i];
sum += v[i];
}
printf("%ld",sum-min);
return 0;
}
long ans = 0;
long l = 1;
{
f0[0][0]=0;
for (long j=0;j<m+1;j++)
{
f0[j][0]=0;
f0[j][1]=-oo;
f1[j][1]=-oo;
f1[j][0]=0;
}
for (long i=1;i<n+1;i++)
{
if ((i&1)==1)
{
for (long j=1;j<m+1;j++)
{
if (j-1<=i-1-l+1)
{
if (f1[j][1]<f0[j-1][0])
f1[j][1]=f0[j-1][0];
if (f1[j][1]<f0[j-1][1]+v[i])
f1[j][1]=f0[j-1][1]+v[i];
}
if (f1[j][0]<f0[j][1])
f1[j][0]=f0[j][1];
if (f1[j][0]<f0[j][0])
f1[j][0]=f0[j][0];
}
for (long j=0;j<m+1;j++)
{
f0[j][1]=-oo;
f0[j][0]=0;
}
}
else
{
for (long j=1;j<m+1;j++)
{
if (j-1<=i-1-l+1)
{
if (f0[j][1]<f1[j-1][0])
f0[j][1]=f1[j-1][0];
if (f0[j][1]<f1[j-1][1]+v[i])
f0[j][1]=f1[j-1][1]+v[i];
}
if (f0[j][0]<f1[j][1])
f0[j][0]=f1[j][1];
if (f0[j][0]<f1[j][0])
f0[j][0]=f1[j][0];
}
for (long j=0;j<m+1;j++)
{
f1[j][1]=-oo;
f1[j][0]=0;
}
}
}
if ( (n&1) ==1)
{
if (ans<f1[m][0])
ans = f1[m][0];
if (ans<f1[m][1])
ans = f1[m][1];
}
else
{
if (ans<f0[m][0])
ans = f0[m][0];
if (ans<f0[m][1])
ans = f0[m][1];
}
}
l = 1;
{
for (long j=0;j<m+1;j++)
{
f0[j][0]=0;
f0[j][1]=-oo;
f1[j][1]=-oo;
f1[j][0]=0;
}
f1[1][1] = v[1];
for (long i=2;i<n+1;i++)
{
if ((i&1)==1)
{
for (long j=1;j<m+1;j++)
{
if (j-1<=i-1-l+1)
{
if (f1[j][1]<f0[j-1][0])
f1[j][1]=f0[j-1][0];
if (f1[j][1]<f0[j-1][1]+v[i])
f1[j][1]=f0[j-1][1]+v[i];
}
if (f1[j][0]<f0[j][1])
f1[j][0]=f0[j][1];
if (f1[j][0]<f0[j][0])
f1[j][0]=f0[j][0];
}
for (long j=0;j<m+1;j++)
{
f0[j][1]=-oo;
f0[j][0]=0;
}
}
else
{
for (long j=1;j<m+1;j++)
{
if (j-1<=i-1-l+1)
{
if (f0[j][1]<f1[j-1][0])
f0[j][1]=f1[j-1][0];
if (f0[j][1]<f1[j-1][1]+v[i])
f0[j][1]=f1[j-1][1]+v[i];
}
if (f0[j][0]<f1[j][1])
f0[j][0]=f1[j][1];
if (f0[j][0]<f1[j][0])
f0[j][0]=f1[j][0];
}
for (long j=0;j<m+1;j++)
{
f1[j][1]=-oo;
f1[j][0]=0;
}
}
}
if ( (n&1) ==1)
{
if (ans<f1[m][1])
ans = f1[m][1];
}
else
{
if (ans<f0[m][1])
ans = f0[m][1];
}
}
printf("%ld",ans);
return 0;
}