题目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 }