题解[SCOI2009]粉刷匠 难度:省选/NOI-
这道题很显然是一道动态规划题,但是我们发现,如果我们想用一个动态规划去做这道题,那么状态和转移都会十分的麻烦,这时候我们就要想是否需要两个动态规划来完成一个复杂的过程,我们看到这道题如果只问每一行的最优结果,那么转移就会很简单,所以我们可不可以将这个问题转化成先求每一行粉刷k次的最优解再求前i行操作k次的最优解。
既然思路确定,那么我们就可以设状态了:1-设置g数组表示在第i行,粉刷j次,刷到k的最优解,2-设置f表示前i行,刷j次的最优解。
转移方程也很好写:1- g[i][j][k]=max(g[i][j][k],max(用前缀和计算在p到k之间最多可以有多少正确的)+g[i][j-1][p]); p是从j-1开始枚举的,具体为什么代码上有注释,k是从j开始的,因为j进行了j次粉刷后最少粉刷到j点。
关于f的是 f[i][j]=max(f[i-1][j-k]+g[i][k][m]).
下面上代码:
#include<iostream> #include<cstdio> using namespace std; int f[101][3001],sum[101][3001]; //f表示前i行刷j次最大对值。 int g[101][3001][101]; //第i行,刷到第j个用k次粉刷得到的最大对值。 int n,m,t; char s[250]; int main(){ ios::sync_with_stdio(0); cin>>n>>m>>t; for(int i=1;i<=n;i++){ cin>>s; sum[i][0]=0; for(int j=1;j<=m;j++){ if(s[j-1]=='1') sum[i][j]=sum[i][j-1]+1; else sum[i][j]=sum[i][j-1]; } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ for(int k=j;k<=m;k++){ for(int p=j-1;p<k;p++){ //因为q是第j-1次粉刷,每一次粉刷至少一格,所以q最少为j-1. g[i][j][k]=max(g[i][j][k],max(sum[i][k]-sum[i][p],k-p-sum[i][k]+sum[i][p])+g[i][j-1][p]); } } } } for(int i=1;i<=n;i++){ for(int j=1;j<=t;j++){ for(int k=0;k<=min(j,m);k++){ f[i][j]=max(f[i][j],f[i-1][j-k]+g[i][k][m]); } } } int ans=0; for(int i=1;i<=t;i++){ ans=max(ans,f[n][i]); } cout<<ans; }