【dp】SCOI2009粉刷匠 纪中集训提高B组
jzoj1035
Description
windy有 N 条木板需要被粉刷。
每条木板被分为 M 个格子。
每个格子要被刷成红色或蓝色。
windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。
每个格子最多只能被粉刷一次。
如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?
一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。
Input
第一行包含三个整数,N M T。
接下来有N行,每行一个长度为M的字符串,'0’表示红色,'1’表示蓝色。
Output
输出一个整数,表示最多能正确粉刷的格子数。
Sample Input
3 6 3
111111
000000
001100
Sample Output
16
Hint
100%的数据,满足 1 <= N,M <= 50 ; 0 <= T <= 2500 。
考场上第一眼以为是贪心。
后面发现不对,觉得是dp,但是它分了很多块木块就不知道怎么处理。(如果是一块整的就大概知道怎么做)
考场上只做了特殊情况,只有10分。
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define N 55
#define ll long long
#define INF 0x3f3f3f3f
int n,m,t;
struct node{
int r[N],b[N],t1,t2;
}a[N];
int tmp[N];
bool cmp(int p,int q)
{
return p>q;
}
int main()
{
int res=0,seg=0;
scanf("%d %d %d",&n,&m,&t);
for(int i=1;i<=n;i++)
{
char s[N];
scanf("%s",s+1);
int cnt=1,j=2;
a[i].t1=0,a[i].t2=0;
while(j<=m)
{
while(s[j]==s[j-1]&&j<=m)
j++,cnt++;
if(s[j-1]=='0') a[i].r[++a[i].r[0]]=cnt,a[i].t1+=cnt;
else a[i].b[++a[i].b[0]]=cnt,a[i].t2+=cnt;
res+=cnt,seg++;
cnt=1,j++;
}
}
if(t<=n)
{
for(int i=1;i<=n;i++)
tmp[i]=max(a[i].t1,a[i].t2);
sort(tmp+1,tmp+n+1,cmp);
int ans=0;
for(int i=1;i<=t;i++)
ans+=tmp[i];
printf("%d\n",ans);
return 0;
}
if(t>=seg)
{
printf("%d\n",res);
return 0;
}
return 0;
}
当初搞完特殊情况之后就在想一般情况,然后就在想怎么给每个木板分配粉刷次数T。如果把T拆成n个正整数的话,感觉这本身就是一个复杂度很高的爆搜2333。然后就不知道怎么处理多块木板了。
结果正解是多次dp,对嘛,不知道怎么处理多块木板的话,就把每块木板当成一个整体再dp一次。
评讲说是分组背包,把每块木板当成一个整体再dp一次那里确实是分组背包,因为每块木板涂的次数是相互冲突的。(不能既涂1次,又涂2次)
感觉这种思想很奇妙,就像是眼光逐渐从局部到整体,先dp每块木板在涂不同次数下的最优值,再把这些木板当成整体,来分配总的T次粉刷。
考场上还贪心地想到最好是相同颜色的肯定是要只用一次刷子(如上程序),但是dp的时候这么写的话细节处理很繁琐,容易写错,所以把每个格子当成一个个体就好。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define N 55
#define M 2005
int f[N][M],g[N][M][N],sum[N][M];
int n,m,t;
/*
f[i][j]:前i行涂j次的最大正确格子数
g[i][j][k]:第i行涂j次 涂到前k个格子的最大数量
//(注意是第i行 真正约束状态的是j&k 有个i只是因为它有很多块木板 f[][]才是连接各个木板关系的)
sum[i][j]:第i行前j个格子中1的数量
*/
int main()
{
scanf("%d %d %d",&n,&m,&t);
for(int i=1;i<=n;i++)
{
char s[105];
scanf("%s",s+1);
sum[i][0]=0;
for(int j=1;j<=m;j++)
sum[i][j]=sum[i][j-1]+s[j]-'0';
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=j;k<=m;k++)//涂j次 至少都会涂到j个
for(int q=j-1;q<=k-1;q++)//枚举涂j-1次时涂到的是哪里
g[i][j][k]=max(g[i][j][k],g[i][j-1][q]+max(sum[i][k]-sum[i][q],k-q/*总格子数*/-(sum[i][k]-sum[i][q])));
for(int i=1;i<=n;i++)
for(int j=1;j<=t;j++)
for(int k=0;k<=min(j,m);k++)//在当前这一行涂k次
f[i][j]=max(f[i][j],f[i-1][j-k]+g[i][k][m]);
printf("%d\n",f[n][t]);
return 0;
}
转载请注明出处,有疑问欢迎探讨
博主邮箱 2775182058@qq.com