1296: [SCOI2009]粉刷匠

Description

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

Input

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

Output

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

Sample Input

3 6 3
111111
000000
001100

Sample Output

16

HINT

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

题解:我觉得这道题的思路还是比较好想的,但是具体落实我自己却不大会,只能。。。
思路是先提前处理好每块木板涂几次时最多能涂几个格子,然后再去讨论前几块木板涂几笔时,所能涂最多的格子,最后前n块木板涂m笔便是答案。
所以这题有两个动归,具体看程序后的注释,会详细解释。
动态转移方程:具体程序看!
#include<iostream>
#include<cstdio>
#include<string>
using namespace std;
int n,m,t,sum,f[100][100],f2[100][3000],tot[100],w[100][100];
char a[100][100],ch;
int max(int a,int b)
{
    if (a>b) return a;
    return b;
}
int main()
{
  cin>>n>>m>>t;
  for (int i=1;i<=n;i++)
  for (int j=1;j<=m;j++)
  {
   cin>>ch;//输入
   a[i][j]=ch;
   }
  for (int k=1;k<=n;k++)//开始一块一块木板处理
  {
      tot[0]=0;
      for (int i=1;i<=m;i++) if (a[k][i]=='0') tot[i]=tot[i-1]+1;
       //记录这块木板前i个格子中有几个格子为0(即红色)
      else tot[i]=tot[i-1];
      for (int i=1;i<=m;i++)
       for (int j=1;j<=m;j++)//f[j][i]表示前j格子涂i笔所能涂的最多格子。//这里要注意要后枚举格子数即j需为格子数。
       {
           f[j][i]=0;
        for (int p=0;p<=j-1;p++)//枚举上一笔的结束处
        {
            sum=tot[j]-tot[p];//p+1——j个格子中为‘0’的个数
            f[j][i]=max(f[j][i],f[p][i-1]+max(j-sum-p,sum));
               //f[p][i-1]指前p个格子涂i-1笔能涂得最多格子数,
               //max(j-sum-p,sum)指选择p+1——j这段区间内涂'0'或涂‘1’所能达到的最优值.
      }
    for (int i=1;i<=m;i++)
    w[k][i]=f[m][i];//w[k][i]和f[m][i]都指这块木板涂i笔时所能涂对的格子数。
  }
  for (int i=1;i<=n;i++)//前i块木板
   for (int j=1;j<=t;j++)//涂几笔
    for (int k=1;k<=j;k++)//第i块木板涂k笔
    if (k<=m) f2[i][j]=max(f2[i-1][j-k]+w[i][k],f2[i][j]);
  cout<<f2[n][t]<<endl;
  return 0;
}
 
原文地址:https://www.cnblogs.com/2014nhc/p/6231190.html