2014.7.8模拟赛【笨笨当粉刷匠】|bzoj1296 [SCOI]粉刷匠

时间:2021-07-24 18:35:06

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