笨笨太好玩了,农田荒芜了,彩奖用光了,笨笨只好到处找工作,笨笨找到了一份粉刷匠的工作。笨笨有n条木板需要被粉刷。每条木板被分成m个格子,每个格子要被刷成红色或蓝色。笨笨每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色,已知每个格子最多只能被粉刷一次。
如果笨笨只能粉刷t次,他最多能正确粉刷多少格子。
一个格子如果未被粉刷或被粉刷成错误颜色,就算粉刷错误。
【输入格式】
第一行三个数n,m,t;
接下来n行,每行一个长度为m的字符“0”表示红色,"1"表示蓝色。
【输出格式】
一个整数,最多能正确粉刷的格子数。
Sample input
3 6 3
111111
000000
001100
Sample output
16
100%数据范围满足1≤n,m≤50;0≤t≤2500。
这是bzoj上原题啊……bzoj1296 [SCOI]粉刷匠
就是两个dp
首先,注意到每一行的状态都是和其他行独立的,也就是说,只要你知道了这一行的状态,其他行长什么样无所谓。
所以我们可以先把每一行分开处理
令s[i][j][0/1]表示第i行前j个有多少个0/1。
g[i][j][k]表示第i行前j个涂k次能正确涂色的格子个数。
则g[i][j][k] = max( g[i][j][k] , g[i][L][k-1]+max(s[i][j][0] - s[i][L][0],s[i][j][1] - s[i][L][1]))。
意思是枚举第k-1次涂到L时,在L到j之间涂0或1是否比当前优,效率n^4。我曾经尝试写n^3求第i行涂j次的最优解,可惜貌似不行。
最后发现再令h[i][j]表示第i行涂j次的最优解,f[i][j]表示前i行涂j次的最优解,则f[i][j]=max(f[i][j] , f[i-1][j-k] + h[i][k])。但是边界范围要考虑,可以一整行都不刷。否则在bzoj上能A,但是模拟赛上只有90。
#include<cstdio> #include<iostream> using namespace std; char ch; int n,m,t; int h[101][101]; int g[101][101][101]; int s[101][101][2]; int f[101][3001]; bool map[101][101]; int main() { freopen("draw.in","r",stdin); freopen("draw.out","w",stdout); scanf("%d%d%d",&n,&m,&t); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) { cin>>ch; if (ch=='1') map[i][j]=1; s[i][j][0]=s[i][j-1][0]+(ch=='0'); s[i][j][1]=s[i][j-1][1]+(ch=='1'); } for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) for (int k=1;k<=m;k++) for (int l=0;l<j;l++) g[i][j][k]=max(g[i][j][k],g[i][l][k-1]+max(s[i][j][0]-s[i][l][0],s[i][j][1]-s[i][l][1])); for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) for (int k=1;k<=m;k++) h[i][j]=max(h[i][j],g[i][k][j]); for (int i=1;i<=n;i++) for (int j=1;j<=t;j++) for (int k=0;k<=j;k++) f[i][j]=max(f[i][j],f[i-1][j-k]+h[i][k]); printf("%d",f[n][t]); }