Description
windy有 N 条木板需要被粉刷。 每条木板被分为 M 个格子。 每个格子要被刷成红色或蓝色。 windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。 如果windy只能粉刷 T 次,他最多能正确粉刷多少格子? 一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。
Input
输入文件paint.in第一行包含三个整数,N M T。 接下来有N行,每行一个长度为M的字符串,’0’表示红色,’1’表示蓝色。
Output
输出文件paint.out包含一个整数,最多能正确粉刷的格子数。
Sample Input
3 6 3
111111
000000
001100
Sample Output
16
HINT
30%的数据,满足 1 <= N,M <= 10 ; 0 <= T <= 100 。 100%的数据,满足 1 <= N,M <= 50 ; 0 <= T <= 2500 。
Solution
我们先一行一行考虑,s[j][k]表示某行最远刷到j,用了k次的最大值。
然后我们可以借助s数组来生成w[k]表示该行刷k次的最大值
就可以像背包一样更新了。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int n,m,t;
char a[55][55];
int f[2505],w[2505];
int zero[55],s[55][2505];
int main()
{
cin>>n>>m>>t;
for(int i=1;i<=n;i++)
{
memset(w,0,sizeof(w));
memset(s,0,sizeof(s));
scanf("%s",a[i]+1);
for(int j=1;j<=m;j++) zero[j]=zero[j-1]+(a[i][j]=='0');
for(int j=1;j<=m;j++)
for(int k=1;k<=t;k++)
{
for(int pre=0;pre<j;pre++)
s[j][k]=max(s[j][k],s[pre][k-1]+max(zero[j]-zero[pre],j-pre-zero[j]+zero[pre]));
w[k]=max(w[k],s[j][k]);
}
for(int j=t;j>=1;j--)
for(int k=1;k<=j;k++)
f[j]=max(f[j],f[j-k]+w[k]);
}
cout<<f[t];
return 0;
}
这个程序美中不足的是,求s的过程最多达到505,有TLE的危险。
我们考虑如何优化s的求法。
上述算法为什么要枚举pre?因为我在状态里没有确定颜色,如果我确定了颜色,我就可以直接从j-1的位置转移了。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int n,m,t;
char a[55][55];
int f[2505],w[2505];
int zero[55],s[55][2505][2];
int main()
{
cin>>n>>m>>t;
for(int i=1;i<=n;i++)
{
memset(w,0,sizeof(w));
memset(s,0,sizeof(s));
scanf("%s",a[i]+1);
for(int j=1;j<=m;j++) zero[j]=zero[j-1]+(a[i][j]=='0');
for(int j=1;j<=m;j++)
for(int k=1;k<=t;k++)
{
s[j][k][0]=max(s[j-1][k][0],s[j-1][k-1][1])+(a[i][j]=='0');
s[j][k][1]=max(s[j-1][k][1],s[j-1][k-1][0])+(a[i][j]=='1');
w[k]=max(w[k],s[j][k][0]);
w[k]=max(w[k],s[j][k][1]);
}
for(int j=t;j>=1;j--)
for(int k=1;k<=j;k++)
f[j]=max(f[j],f[j-k]+w[k]);
}
cout<<f[t];
return 0;
}