「Luogu4158」[SCOI2009]粉刷匠

problem

题目描述

windywindyNN 条木板需要被粉刷。 每条木板被分为 MM 个格子。 每个格子要被刷成红色或蓝色。

windywindy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。

如果windywindy只能粉刷 TT 次,他最多能正确粉刷多少格子?

一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。

输入输出格式

输入格式:

第一行包含三个整数,NN MM TT

接下来有NN行,每行一个长度为MM的字符串,'0’表示红色,'1’表示蓝色。

输出格式:

包含一个整数,最多能正确粉刷的格子数。

输入输出样例

输入样例#1:

1
2
3
4
3 6 3
111111
000000
001100

输出样例#1:

1
16

说明

30%30\%的数据,满足 1N,M101 \le N,M \le 100T1000 \le T \le 100

100%100\%的数据,满足 1N,M<=501 \le N,M <= 500T25000 \le T \le 2500

Solution

蒟蒻看到这题想了nn多种完全不正确的处理方法,如果是在考场上估计已经光速凉凉了QAQQAQ

对于每一行,我们可以把它分成若干个颜色不同的连续段(对应若干次颜色不同的粉刷),从左到右考虑

注意到题目:

一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。

意思就是不刷白不刷

如果是颜色不对,多刷这一格不会比不刷差,就不需要考虑不刷的情况

所以我们有:

状态:设dpi,j,k,0/1dp_{i,j,k,0/1}表示当前枚举到第ii行,第jj个格子,已经涂了kk次(分成了kk段),当前格子涂的颜色是0/10/1

方程:

dpi,j,k,x=max(dpi,j1,k,x,dpi,j1,k1,x xor 1)+[x=colori,j]dp_{i,j,k,x}=max(dp_{i,j-1,k,x},dp_{i,j-1,k-1,x\space xor\space 1})+[x=color_{i,j}]

(方括号是艾弗森括号,当其中的条件为真时值为11,否则为00)

这个转移应该很好理解吧


处理完每一行之后,max(dpi,m,k,0,dpi,m,k,1)max(dp_{i,m,k,0},dp_{i,m,k,1})即为第ii行分配kk次粉刷次数所能正确粉刷的最大格子数

这样,每一行作为一组,原问题就转化为一个分组背包问题,在此不再赘述

Code

代码中所用的字母与上述转移方程略有不同,请注意辨别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define maxn 55
#define maxm 55
#define maxt 2505
using namespace std;
typedef long long ll;

template <typename T> void read(T &t)
{
t=0;int f=0;char c=getchar();
while(!isdigit(c)){f|=c=='-';c=getchar();}
while(isdigit(c)){t=t*10+c-'0';c=getchar();}
if(f)t=-t;
}

int n,m,t;
int col[maxn][maxm];
int dp[maxn][maxm][maxm][2],tdp[maxt];

int main()
{
read(n),read(m),read(t);
for(register int i=1;i<=n;++i)
{
char c[maxm];
scanf("%s",c+1);
for(register int j=1;j<=m;++j)
col[i][j]=c[j]-'0';
}
for(register int li=1;li<=n;++li)
for(register int i=1;i<=m;++i)
for(register int k=1;k<=min(t,m);++k)
for(register int x=0;x<=1;++x)
dp[li][i][k][x]=max(dp[li][i-1][k][x],dp[li][i-1][k-1][x^1])+(x==col[li][i]);
for(register int i=1;i<=n;++i)
for(register int j=t;j>=0;--j)
for(register int k=1;k<=min(j,m);++k)
tdp[j]=max(tdp[j],tdp[j-k]+max(dp[i][m][k][0],dp[i][m][k][1]));
printf("%d",tdp[t]);
return 0;
}