题目描述
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
S o l u c i o ˊ n d e l p r o b l e m a \mathrm{Solución\ del\ problema} Solucioˊn del problema
由于错误粉刷不扣分,所以每一次粉刷对答案的最大贡献就是粉刷区间内 max ( 1 的 个 数 , 0 的 个 数 ) \max(1的个数,0的个数) max(1的个数,0的个数),可以利用前缀和求出 1 1 1 的个数,用总数减去它就是 0 0 0 的个数,记为 s [ k ] [ i ] s[k][i] s[k][i] ,表示第 k k k 行前 i i i 个数的前缀和。
观察可知每一条木板(每一行)都是独立的,如果我们知道每一行分别用了多少粉刷次数,就可以用dp求解每一行的对答案的最大贡献。
故对于每一行,设
g
[
k
]
[
i
]
[
j
]
g[k][i][j]
g[k][i][j] 表示在第
k
k
k 行,前
i
i
i 个格子用
j
j
j 次粉刷的最大贡献,则可以将前
i
i
i 个格子分成两段,前半段用
j
−
1
j-1
j−1 次粉刷,用半段单独用
1
1
1 次。
g
[
k
]
[
i
]
[
j
]
=
max
j
−
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
−
1
i-1
i−1 个格子时第
j
j
j 次粉刷的区间往后多刷一格即可,即在这个方程的转移下得到的
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].
因此,我们可以将原问题转化为:
有 N N N 组物品和一个容积为 T T T 的背包,每个物品有一个容积 j j j 和一个价值 g [ k ] [ m ] [ j ] g[k][m][j] g[k][m][j] ,每组只能选一个放入背包,求最大价值和。
套用分组背包即可。
C o ˊ d i g o \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;
}