格子染色

【题目描述】

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

【输入描述】

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

如果只能粉刷t次,询问最多能正确粉刷多少格子。

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

【输出描述】

第一行包含三个整数,n、m、t。接下来有n行,每行一个长度为m的字符串,0表示红色,1表示蓝色。

【样例输入】

3 6 3
111111
000000
001100

【样例输出】

16

【数据范围及提示】

1 ≤ n,m ≤ 50,0 ≤ t ≤ 2500。

源代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,T,ans(0),Sum[2501],f[2501][2501],DP[2501][2501]; //Sum[i]=该行前i个格子是1的格子的数目。
char i[2501];
int main()
{
    scanf("%d%d%d",&n,&m,&T);
    for(int k=1;k<=n;k++)
    {
        scanf("%s",i+1);
        for (int a=1;a<=m;a++)
          Sum[a]=Sum[a-1]+(i[a]=='1');
        for (int a=1;a<=m;a++)
          for(int b=1;b<=m;b++)
          {
            f[b][a]=0; //取最大值先赋最小值。
            for (int c=0;c<b;c++) //可以为0,因为可以前面不走。
            {
                int Num=Sum[b]-Sum[c]; //前缀和处理,意为[c,b]之中1的个数。
                f[b][a]=max(f[b][a],f[c][a-1]+max(Num,b-c-Num)); //取0或1的正确格子数最大值。
            }
          }
        for (int a=1;a<=T;a++)
        {
            int Num=min(m,a); //Num意为实际粉刷次数,在一行的格子数与粉刷次数限制中取小。
            for (int b=1;b<=Num;b++)
              DP[k][a]=max(DP[k][a],DP[k-1][a-b]+f[m][b]);
        }
    }
    printf("%d",DP[n][T]);
    return 0;
}

/*
    很有意思的一个划分型DP,这种类型的动态规划总是想不出来,解法又令人拍案称奇。
    解题思路:
        先处理当前行,设f[i][j]为该行前i个格子刷j次最多的正确格子数,则有:
            f[i][j]=max(f[k][j-1]+max(T1,T2)),其中k表示当前行这一次粉刷的起点,T1、T2分别表示当前区间0、1的格子数;
        再进行大局处理,设DP[i][j]为前i行刷了j次的最多正确格子数,则有:
            DP[i][j]=max(DP[i-1][j-k]+f[m][k]),其中k表示当前行粉刷的次数。
*/
原文地址:https://www.cnblogs.com/Ackermann/p/5823225.html