粉刷匠
大致题意:
n个木板,每个木板m个格子,目标的格子只有两种颜色,'0’表示粉色,'1’表示蓝色.
每次只能粉刷一个木板上连续的格子,涂上同一种颜色(每个格子只能被粉刷一次)
求T次粉刷后,粉刷正确颜色的最大格子数
(一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷)说明所有格子都会被粉刷
解题思路:
状态表示: f[i][j] 表示前i个木板,粉刷j次的正确粉刷的最大格子数
g[i][j][k]表示第i个木板,粉刷j次,涂了前k个格子的正确粉刷的最大格子数
sum[i][j]表示第i个木板前j个格子是蓝色格子的个数
sum[i][j]的转移方程很简单,当s[i]=='1’时,sum[i][j]=sum[i][j-1]+1
对g数组来说,就是对第j次粉刷进行讨论,也就是说q(格子数)在区间[j-1,k)内进行转移,(j-1是最小粉刷格子数):
j-1次粉刷前q个粉刷正确的格子数+第j次粉刷q到k中蓝色格子和粉色格子数目的最大值
sum[i][k] - sum[i][q]是蓝色格子数量,k - q - sum[i][k] + sum[i][q]是粉色格子数量
转移方程: g[i][j][k] = max(g[i][j][k], g[i][j - 1][q] + max(sum[i][k] - sum[i][q], k - q - sum[i][k] + sum[i][q]))
f数组就是对第i个木板进行k次粉刷进行讨论
转移方程:f[i][j] = max(f[i][j], f[i - 1][j - k] + g[i][k][m])
最后求一下f[n][i]的最大值即可
AC代码:
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
#define debug(a) cout << #a << " = " << a << endl;
using namespace std;
typedef long long ll;
int n, m, t;
char s[155];
int f[55][2510]; //前i个木板,粉刷j次的正确粉刷的最大格子数
int g[55][2510][55]; //第i个木板,粉刷j次,涂了前k个格子的正确粉刷的最大格子数
int sum[55][55]; //蓝色的格子数
int main(void)
{
cin >> n >> m >> t;
for (int i = 1; i <= n; ++i) {
cin >> s + 1;
sum[i][0] = 0;
for (int j = 1; j <= m; ++j) {
if (s[j] == '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 = 1; k <= m; ++k) { //格子数
for (int q = j - 1; q < k; ++q) { //格子数
//判断j-1到k区间蓝色格子多还是粉色格子多
int tmp = max(sum[i][k] - sum[i][q], k - q - sum[i][k] + sum[i][q]);
g[i][j][k] = max(g[i][j][k], g[i][j - 1][q] + tmp);
}
}
}
}
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 res = 0;
for (int i = 1; i <= t; ++i)res = max(res, f[n][i]);
cout << res << endl;
return 0;
}