牛客网 【每日一题】[SCOI2009]粉刷匠
题目描述
windy有 N 条木板需要被粉刷。 每条木板被分为 M 个格子。 每个格子要被刷成红色或蓝色。
windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。 如果windy只能粉刷 T
次,他最多能正确粉刷多少格子? 一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。
输入描述:
输入文件paint.in第一行包含三个整数,N M T。 接下来有N行,每行一个长度为M的字符串,'0’表示红色,'1’表示蓝色。
输出描述:
输出文件paint.out包含一个整数,最多能正确粉刷的格子数。
示例1
输入
3 6 3
111111
000000
001100
输出
16
题意:
n个木板,有m个格子,能粉刷t次,每次可以粉刷连续的一段,问最多能正确粉刷多少个格子?
题解:
怎么涂才最大化?其实全涂最好,为什么?反正涂错没惩罚,涂对就算赚,但是我们只能涂t次,所以就是t行涂满
dp的做法
有二维dp和四维dp的做法
二维dp
就是一个分组背包求解
预处理每块木板的最优解
因为木板只有0或1,所以我们将1统计出来,剩下的就是0
可以用到前缀和来统计1的数量
pre[i]表示前i块中1的数量
dp [ i ] [ j ] 表示前i块木板粉刷j次最多可以刷的格子数
我们根据题意可以得到转移方程
pree=pre[r]-pre[l]//表示区间l到r之间1的数量
f [ r] [ j ] = max ( f [ r ] [ j ] [ , f [ l ] [ j-1 ] + max ( pree ,r-l-pree))
怎么理解?
前r块的木板粉刷j次,是前l块木板粉刷j-1次,再加上l与r之间的最大数(这个最大数是指1和0哪个出现的次数最多,这样就粉刷数量多的那个颜色)
代码略
四维dp
dp [ i ] [ j ] [ k ] [ 1 / 0] 表示前i条第j段涂了k次,涂成红(0)或蓝(1)的最多格子数
如果涂的是当前木板第一段(也就是j=1),就要接着上一个木板转移:
dp[i][j][k][0]=if(a[i][j]==′0 ′)+max(dp[i−1][m][k−1][0],dp[i−1][m][k−1][1])
第i块木板的第一段是由当前木板的颜色加上上块木板的最大值情况
dp[i][j][k][1] = if(a[i][j] == ‘1’)+max(dp[i-1][m][k-1][0],dp[i-1][m][k-1][1])
如果不是当前木板第一段,那就是同块木板的上一段承接而来
dp[i][j][k][0]=if(a[i][j]== ′0′ ) + max(dp[i][j−1][k][0],dp[i][j−1][k−1][1])
dp[i][j][k][1] = if(a[i][j] == ’ 1 ') + max(dp[i][j-1][k-1][0],dp[i][j-1][k][1])
本段木板颜色直接承接前一段的最大值
答案就是看红(0)或蓝(1)哪个最多
dp[n][m][t][0/1]
看到一个大佬用的滚动数组压维(秒啊)
滚动数组压维版代码
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
for (int k = 1; k <= t; k++)
{
if (j == 1)
dp[i & 1][j][k][1] = max(dp[(i - 1) & 1][m][k - 1][0], dp[(i - 1) & 1][m][k - 1][1]) + (a[i][j] == 1 + '0');
else
dp[i & 1][j][k][1] = max(dp[i & 1][j - 1][k][1], dp[i & 1][j - 1][k - 1][1 ^ 1]) + (a[i][j] == 1 + '0');
if (j == 1)
dp[i & 1][j][k][0] = max(dp[(i - 1) & 1][m][k - 1][0], dp[(i - 1) & 1][m][k - 1][1]) + (a[i][j] == 0 + '0');
else
dp[i & 1][j][k][0] = max(dp[i & 1][j - 1][k][0], dp[i & 1][j - 1][k - 1][0 ^ 1]) + (a[i][j] == 0 + '0');
}