题目描述
windy有 N 条木板需要被粉刷。 每条木板被分为 M 个格子。 每个格子要被刷成红色或蓝色。
windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。
如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?
一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。
输入格式
第一行包含三个整数,N M T。
接下来有N行,每行一个长度为M的字符串,'0’表示红色,'1’表示蓝色。
输出格式
包含一个整数,最多能正确粉刷的格子数。
输入输出样例
输入 #1
3 6 3
111111
000000
001100
输出 #1
16
说明/提示
30%的数据,满足 1 <= N,M <= 10 ; 0 <= T <= 100 。
100%的数据,满足 1 <= N,M <= 50 ; 0 <= T <= 2500
Solucioˊn del problema\mathrm{Solución\ del\ problema}Solucioˊn del problema
由于错误粉刷不扣分,所以每一次粉刷对答案的最大贡献就是粉刷区间内 max(1的个数,0的个数)\max(1的个数,0的个数)max(1的个数,0的个数),可以利用前缀和求出 111 的个数,用总数减去它就是 000 的个数,记为 s[k][i]s[k][i]s[k][i] ,表示第 kkk 行前 iii 个数的前缀和。
观察可知每一条木板(每一行)都是独立的,如果我们知道每一行分别用了多少粉刷次数,就可以用dp求解每一行的对答案的最大贡献。
故对于每一行,设 g[k][i][j]g[k][i][j]g[k][i][j] 表示在第 kkk 行,前 iii 个格子用 jjj 次粉刷的最大贡献,则可以将前 iii 个格子分成两段,前半段用 j−1j-1j−1 次粉刷,用半段单独用 111 次。
g[k][i][j]=maxj−1≤t<i(g[k][i][j],g[k][t][j−1]+max(s[i]−s[t],(i−t)−(s[i]−s[t])) )g[k][i][j]=\max\limits_{j-1\le t<i}(g[k][i][j],g[k][t][j-1]+\max(s[i]-s[t],(i-t)-(s[i]-s[t]))\ )g[k][i][j]=j−1≤t<imax(g[k][i][j],g[k][t][j−1]+max(s[i]−s[t],(i−t)−(s[i]−s[t])) )
目标:g[k][m][j]g[k][m][j]g[k][m][j]
此时可以不需要考虑 g[k][i−1][j]g[k][i-1][j]g[k][i−1][j] 到 g[k][i][j]g[k][i][j]g[k][i][j] 的转移,因为我们只需将粉刷前 i−1i-1i−1 个格子时第 jjj 次粉刷的区间往后多刷一格即可,即在这个方程的转移下得到的 g[k][i][j]≥g[k][i−1][j]g[k][i][j]\ge g[k][i-1][j]g[k][i][j]≥g[k][i−1][j].
因此,我们可以将原问题转化为:
有 NNN 组物品和一个容积为 TTT 的背包,每个物品有一个容积 jjj 和一个价值 g[k][m][j]g[k][m][j]g[k][m][j] ,每组只能选一个放入背包,求最大价值和。
套用分组背包即可。
Coˊdigo\mathrm{Código}Coˊdigo
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=55,M=2505;
int r,c,m;
int s[N][N];
int g[N][N][N],f[M];
int main()
{
scanf("%d%d%d",&r,&c,&m);
for(register int i=1;i<=r;i++)
{
char str[N];scanf("%s",str+1);
for(register int j=1;j<=c;j++) s[i][j]=s[i][j-1]+str[j]-'0';
}
for(register int k=1;k<=r;k++)
for(register int i=1;i<=c;i++)
for(register int j=1;j<=i;j++)
for(register int t=j-1;t<i;t++)
g[k][i][j]=max(g[k][i][j],g[k][t][j-1]+max(s[k][i]-s[k][t],(i-t)-(s[k][i]-s[k][t])));
for(register int i=1;i<=r;i++)
for(register int j=m;j>=0;j--)
for(register int k=1;k<=min(j,c);k++)
f[j]=max(f[j],f[j-k]+g[i][c][k]);
printf("%d\n",f[m]);
return 0;
}