题意:
第一行输入样例数,每个样例的第一行输入有n天,接下来的n行是每天如果学习对应技能会获得的提升点数,我们的帅yao先生有三个技能,泡妞,打lol,写题。Yao每天可以对其中一个技能进行精进,获得的提升是这一天这个技术对应的提升点数,如果我k天没有学这个技能了,那这一天还不学这个技能,我这个技能就会掉k点数,也就是两天没学的话会在第一天掉一点,第二天再掉两点这样。然后问我们如何合理的每天学习技能可以让我们最后技能点数总和最大(点数不会小于0)
思路:
首先一点一点要想到的是他如果学了后期长久不学了忘到0了肯定不是最优的,因为忘到0了就等于没学,就亏操作了。所以我们最优的一定是学了之后这个不会降为0转移过来的,因为我们取得是最优,所以这些会变成0的非最优解不会影响我们最后的答案,
我们设置我们的f[i][j][k][z]表示前i个数里,第一个技能有j天未学过,第二个技能有k天未学过,第三个技能有z天没学过,为之后来看我们四维的话数组会爆,考虑到我们有一个技能始终是刚学的,那这四维其实可以转变为第四维记录我们学了哪一个技能,这个没学过的时间一定是0天,其他两维是另外两个多久没学了。代码里有注释,细看代码。
Code:
int dp [1001][202][202][3];//第一维度是n,第二维和第三维是我们两个不是今天
//学的的技能没有被学过的天数,第三维是今天学的是哪一个技能,第1/2/3种.
void solve()
{
int n;
cin>>n;
int a[n][3];
for(int i=0;i<n;i++)
{
for(int j=0;j<3;j++)
{
cin>>a[i][j];
}
}
for(int i=0;i<=n;i++){
for(int j=0;j<=201;j++){
for(int k=0;k<=201;k++){
for(int l=0;l<3;l++){
dp[i][j][k][l] = 0;//初始化
}
}
}
}
int ans=0;
for(int i=0;i<=n;i++)
//我们是从0循环到了n;其实每次的扣分是在下一次循环的开头去给他扣的,
//也就是是说我们第一天循环扣的会在下一天开始的时候给我们扣掉,所以我们
//总共循环了n+1次,最后一次n+1次的时候就会扣掉最后一天的并且跳过后面的所有操作
{
for(int j=0;j<=200;j++){
for(int k=0;k<=200;k++){
for(int l=0;l<3;l++){
int x = l;//将l=1/2/3的情况带入依次得到;
int x1=(l+1)%3;
int x2=(l+2)%3;
int re=dp[i][j][k][l];
if(j<200)
{
re-=j;//有j天没学这个技能了,所以总和先剪掉j
}
if(k<200)
{
re-=k;//有k天没学这个技能,先-k
}
if(i==n)
{//我们第n+1次时扣完直接结算答案,跳出,不走后面操作了
ans=max(ans,re);
continue;
}
int z=200;
int xk=min(z,k+1);//这是时间往后加一天
int xj=min(z,j+1);
dp[i+1][xj][xk][x]=max(dp[i+1][xj][xk][x],re+a[i][x]);//学了第x种,然后昨天也学的时第x种;
dp[i+1][xk][1][x1]=max(dp[i+1][xk][1][x1],re+a[i][x1]);//学了x1种,然后昨天学的另一种,即为1天
dp[i+1][1][xj][x2]=max(dp[i+1][1][xj][x2],re+a[i][x2]);//学了x2,然后昨天学的另一种,即为1天
}
}
}
}
cout<<ans<<endl;
}