算法入门-[SCOI2009]粉刷匠
[SCOI2009]粉刷匠
https://ac.nowcoder.com/acm/problem/20273
#动态规划 #多次dp
题意
- 有n块木板,每块木板有m格,你可以粉刷t次,每次可以粉刷连续的若干格,不能覆盖
- 给定正确的粉刷方案,请问最多可以刷对多少格
0<=n,m<=50,t<=2500
思路
- 如果只有一块木板,可以线性dp解决
表示刷i次,刷到j
- 对于若干块板子,如果能在O(1)的时间知道对于任意一块板子刷到某一个程度的正确格数就可以dp
- 预处理每一块木板的价值
- 再dp任意块的价值
- 注意考虑每个循环的意义确定范围
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
/**
* 对于一块板子
* 用前缀和维护刷一次能刷对多少max(sum[r]-sum[l-1],(r-l+1)-sum[r]-sum[l-1])
* f[i][j]表示刷i次,刷到j
* f[i][j]=f[i-1][l]+sum(l,j)
* 加一维,维护每一个板子
* 对于n块板子
* G[k][i][j]表示前k块板子,刷i次,最后一块板子刷j次
* G[k][i][j]=G[k-1][i-j][p]
*/
const int N=60;
int mp[N][N],sum[N][N],f[N][3000][N],g[N][3000][N];
int cal(int idx,int l,int r){
return max(sum[idx][r]-sum[idx][l-1],(r-l+1)-sum[idx][r]+sum[idx][l-1]);
}
signed main(){
int n,m,t;
cin >> n >> m >> t;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%1d",&mp[i][j]);
sum[i][j]=sum[i][j-1]+mp[i][j];
}
}
for(int cnt=1;cnt<=n;cnt++){
for(int i=1;i<=t;i++){//存在i-1,i必须从1开始
for(int j=1;j<=m;j++){
for(int l=0;l<=j;l++){//上一步可以从0开始,表示什么都没刷
f[cnt][i][j]=max(f[cnt][i][j],f[cnt][i-1][l]+cal(cnt,l+1,j));
}
}
}
// cout << f[cnt][t][m] << endl ;
// for(int i=1;i<=t;i++){
// for(int j=1;j<=m;j++){
// cout << f[cnt][i][j] << ' ';
// }
// cout << endl;
// }
}
for(int k=1;k<=n;k++){
for(int i=1;i<=t;i++){
for(int j=1;j<=m;j++){//最后一次刷的如果是0就没意义
for(int p=0;p<=m;p++){//上一次刷的可以是0表示开始
if(j>i) continue;
g[k][i][j]=max(g[k][i][j],g[k-1][i-j][p]+f[k][j][m]);
}
}
}
}
int ans=0;
for(int i=0;i<=m;i++){
ans=max(ans,g[n][t][i]);
}
cout << ans << endl;
return 0;
}