先放题面
先简单说说自己最开始的wa解法:对所有木板直接dp,只能从该木板的长度内或其他木板的末尾转移过来。dp方程定义为:dp[k][i][j]表示刷k次、在第i个木板的第j格结束。结果后来对拍后才发现这个有后效性,挂了。
正解:
对每一个木板先进行一次dp,求出第i个木板刷k次最多能刷对多少格。然后再拿每个木板作为一个组,进行分组背包(其实将木板作为一个泛化物品进行背包问题会理解的更形象?)
然后有一个小小的优化:
在dp时,我们需要枚举之前的状态来选择最优解。而这一重枚举可能会造成超时(没试过),所以希望把它优化掉。用不着单调队列优化,将需枚举的量和在一起,每次取一个最值保存下来
就像这样:
f[k][i][j]=max(cnt[0][i][j]+tmp0,cnt[1][i][j]+tmp1);
tmp0=max(tmp0,f[k-1][i][j]-cnt[0][i][j]);
tmp1=max(tmp1,f[k-1][i][j]-cnt[1][i][j]);
完整代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char c[55][55];
int n,m,T,cnt[2][55][55],f[3000][55][55],dp[3000][55];
int main(){
scanf("%d%d%d",&n,&m,&T);
for(int k=1;k<=n;k++){
scanf("%s",c[k]+1);
for(int i=1;i<=m;i++){
cnt[0][k][i]=cnt[0][k][i-1]+(c[k][i]=='0');
cnt[1][k][i]=cnt[1][k][i-1]+(c[k][i]=='1');
}
}
int tmp0,tmp1;
for(int k=1;k<=m;k++){
for(int i=1;i<=n;i++){
tmp0=tmp1=0;
for(int j=1;j<=m;j++){
f[k][i][j]=max(cnt[0][i][j]+tmp0,cnt[1][i][j]+tmp1);
tmp0=max(tmp0,f[k-1][i][j]-cnt[0][i][j]);
tmp1=max(tmp1,f[k-1][i][j]-cnt[1][i][j]);
}
}
}
for(int k=1;k<=T;k++){
for(int i=1;i<=n;i++){
dp[k][i]=max(dp[k][i],dp[k][i-1]);
for(int j=1;j<=min(k,m);j++)
dp[k][i]=max(dp[k][i],dp[k-j][i-1]+f[j][i][m]);
}
}
printf("%d\n",dp[T][n]);
return 0;
}