CDQZ_Training 2012-5-24 笨笨当粉刷匠

题目OJ地址:http://cdqz.openjudge.cn/noip/1010/

 

问题摘要:

    笨笨太好玩了,农田荒芜了,彩奖用光了,笨笨只好到处找丁作,笨笨找到了一份粉刷匠的工作。笨笨有n条木板需要被粉刷。每条木板被分成m个格子,每个格子要被刷成红色或蓝色。笨笨每次粉刷,只能选择  一条木板上一段连续的格子,然后涂上一种颜色,已知每个格子最多只能被粉刷一次。

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

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

    【输入格式】

    第一行三个数n,m,t;

    接下来n行,每行一个长度为m的字符“O”表示红色,“l”表示蓝色。

    【输出格式】

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

Sample input

    3 6 3

    111111

    000000

    001100

Sample output

    16

 【数据范围】

 100%数据范围满足l≤n.m≤50:0≤t≤2500。

  话说这题真心水啊,比第一题还水……就是一个两次DP的问题,方程也很容易想到,这也是我本次考试唯一一道一次AC的题。当然有一定的原因是之前做过,参考了下网上的方程……不过最终还是独立做出来了,而且理解了这个方程。其实DP题做多了,自己都该明白方程的大致趋向(除了数位DP我至今打不着方向……)     设g[i][j]代表每一行前i个格子刷j下所能刷对的最大格数,则每一层就成了一个区间DP的问题。把每一层要求涂的颜色分成若干黑白相间的区,可以很容易知道方程:g[i][j]=max{g[i-l][p]+w[p+l][j]}(w[l][r]代表从第l到第r格粉刷一次最多能刷对的格数,0<=p<i)     关于上面方程的理解,其实可以这样说:在j-1次粉刷的基础上再分段一次(即把其中的一次粉刷断开为两次粉刷,p为断点),所能增加的刷对格数。为了减小复杂度,可以把连续相同颜色的划为一块,这样p的移动幅度大些。     完成每一行的计算以后,每一个g[i][j]的值就成了一个0,1背包问题中的物品,便可轻松解决。(不懂0,1背包的自己百度,这太基础了……)   注意:最好采用滚动数组以节约空间。   废话不多说,上代码:
View Code
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 const int whcissb=0x7fffffff/2;
 6 
 7 char sbwhc[60][60];
 8 int size[60];
 9 int sb[4025][2600][3];
10 int w[4025][2];
11 int n,m,t;
12 
13 inline void sylissb(int &a,const int &b)
14 {
15     if(b>a) a=b;
16 }
17 
18 int main()
19 {
20     freopen("draw.in","r",stdin);
21     freopen("draw.out","w",stdout);
22 
23     scanf("%d%d%d",&n,&m,&t);
24     for(int i=0,tot=0;i<n;i++)
25     {
26         scanf(" %s",sbwhc[i]);
27         for(int j=1,k=1;j<=m;j++)
28         {
29             if(sbwhc[i][j]!=sbwhc[i][j-1])
30             {
31                 if(sbwhc[i][j-1]=='0') w[tot][0]=k;
32                 else w[tot][1]=k;
33 
34                 tot++;
35                 size[i]++;
36                 k=1;
37             }
38             else k++;
39         }
40     }
41 
42     for(int i=0,tot=0;i<n;i++)
43     {
44         for(int j=tot;j<tot+size[i];j++)
45             for(int k=0;k<=t;k++)
46                 sb[j][k][0]=sb[j][k][1]=-whcissb;
47 
48         tot+=size[i];
49     }
50 
51     int tot=0;
52     for(int i=0;i<n;i++)
53     {
54         if(tot)
55         {
56             for(int k=1;k<=t;k++)
57             {
58                 sb[tot][k][0]=sb[tot][k][1]=max(max(sb[tot-1][k-1][0],sb[tot-1][k-1][1]),sb[tot-1][k-1][2]);
59                 sb[tot][k][0]+=w[tot][0];
60                 sb[tot][k][1]+=w[tot][1];
61                 sb[tot][k][2]=max(max(sb[tot-1][k][0],sb[tot-1][k][1]),sb[tot-1][k][2]);
62             }
63         }
64         else for(int k=1;k<=t;k++)
65         {
66             sb[0][k][0]=w[0][0];
67             sb[0][k][1]=w[0][1];
68         }
69 
70         for(int j=tot+1;j<tot+size[i];j++)
71         {
72             for(int k=1;k<=t;k++)
73             {
74                 sb[j][k][0]=sb[j-1][k][0]+w[j][0];
75                 if(k) sylissb(sb[j][k][0],sb[j-1][k-1][1]+w[j][0]);
76 
77                 sb[j][k][1]=sb[j-1][k][1]+w[j][1];
78                 if(k) sylissb(sb[j][k][1],sb[j-1][k-1][0]+w[j][1]);
79 
80                 sb[j][k][2]=max(max(sb[j-1][k][0],sb[j-1][k][1]),sb[j-1][k][2]);
81             }
82         }
83         tot+=size[i];
84     }
85 
86     printf("%d\n",max(max(sb[tot-1][t][0],sb[tot-1][t][1]),sb[tot-1][t][2]));
87     return 0;
88 }

 

 

转载于:https://www.cnblogs.com/stickjitb/archive/2012/05/25/2518739.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值