[BZOJ1296]&&[SCOI2009] 粉刷匠

描述 Description

windy 有 N 条木板需要被粉刷。 每条木板被分为 M 个格子。 每个格子要被刷成红色或蓝色。 windy 每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。 如果 windy 只能粉刷 T 次,他最多能正确粉刷多少格子? 一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。

输入格式 InputFormat

输入文件 paint.in 第一行包含三个整数,N M T。 接下来有 N 行,每行一个长度为 M 的字符串,’0’表示红色,’1’表示蓝色。

输出格式 OutputFormat

输出文件 paint.out 包含一个整数,最多能正确粉刷的格子数。

样例输入 SampleInput

3 6 3
111111
000000
001100

样例输出 SampleOutput

16

数据范围和注释 Hint

30% 的数据,满足 1 <= N,M <= 10 ; 0 <= T <= 100 。

100% 的数据,满足 1 <= N,M <= 50 ; 0 <= T <= 2500 。


BZOJ 1296


代码 Code

二次动规。\(c[i,j,0..1]\) 表示:第 i 块板、前 j 个格子中 0 或 1 的个数。\(g[i,j,k]\) 表示:第 i 块板、粉刷 j 次、前 k 个格子、最多能粉刷正确的格子数。\(f[i,j]\) 表示:前 i 块板、粉刷 j 次、最多能粉刷正确的格子数。

\[ g[i,j,k]=Max \lbrace g[i,j-1,t]+Max \lbrace \begin{align} &c[i,k,0]-c[i,t,0] \\\\ &c[i,k,1],c[i,t,1] \end{align} \rbrace \rbrace,(0\leq t<k). \\\\ f[i,j]=Max \lbrace f[i-1,k]+g[i,j-k,m] \rbrace,(0\leq t \geq j). \]

#include <stdio.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int inf=0x7fffffff;
int c[55][55][2],g[55][2505][55],f[55][2505];
int i,j,t,n,m,l,r,k,q,z,y,x;
char ch;
inline int cort(int s,int x,int y)
{
    return max(c[s][y][0]-c[s][x-1][0],c[s][y][1]-c[s][x-1][1]);
}
int main()
{
    memset(c,0,sizeof(c));
    memset(f,-1,sizeof(f));
    memset(g,0,sizeof(g)); 
    scanf("%d%d%dn",&n,&m,&q);
    if (n*m<=q)
    {
        printf("%dn",n*m);
        return 0;
    }
    for (i=1;i<=n;i++)
    {
        for (j=1;j<=m;j++)
        {
            ch=getchar();
            c[i][j][ch-'0']=c[i][j-1][ch-'0']+1;
            c[i][j][(ch-'0')^1]=c[i][j-1][(ch-'0')^1];
        }
        ch=getchar();
    }
    f[0][0]=0;
    for (i=1;i<=n;i++) for (j=1;j<=m;j++) for (k=1;k<=m;k++)
    {
        for (t=0;t<k;t++) g[i][j][k]=max(g[i][j][k],g[i][j-1][t]+cort(i,t+1,k));
    }
    for (i=1;i<=n;i++) for (j=0;j<=q;j++)
    {
        for (k=0;k<=j;k++) if (f[i-1][k]>=0) f[i][j]=max(f[i][j],f[i-1][k]+g[i][j-k][m]);
    }
    printf("%dn",f[n][q]);
    return 0;
}