zhber
有好多做过的题没写下来,如果我还能记得就补吧

 

 笨笨太好玩了,农田荒芜了,彩奖用光了,笨笨只好到处找工作,笨笨找到了一份粉刷匠的工作。笨笨有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]);
}

 

posted on 2014-07-08 22:47  zhber  阅读(271)  评论(0编辑  收藏  举报