题目描述
windy有 N 条木板需要被粉刷。 每条木板被分为 M 个格子。 每个格子要被刷成红色或蓝色。
windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。
如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?
一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。
输入描述
第一行包含三个整数,N M T。
接下来有N行,每行一个长度为M的字符串,'0’表示红色,'1’表示蓝色。
30%的数据,满足 1 <= N,M <= 10 ; 0 <= T <= 100 。
100%的数据,满足 1 <= N,M <= 50 ; 0 <= T <= 2500 。
输出描述
输出包含一个整数,最多能正确粉刷的格子数。
示例
输入
3 6 3
111111
000000
001100
输出
16
题目思路
首先一块木板要刷的话一定会全部刷完,因为不刷或者刷错了都不算数,所以干脆刷完,即使刷错了也不要紧,有可能还能多刷对几块。例如001100,刷一次,全刷红色,就能有4块对的。
再分析一块木板刷k次最多能正确粉刷多少格子呢?
我们可以先把这块木板连续续相同颜色格子合并起来,例如001100这块板子看作2 2 2,代表有三块连续的相同颜色块数,且相邻两块颜色不同。很容易想到这块木板的粉刷一定是刷红刷蓝交替进行的,且后一次粉刷可以利用前一次粉刷的结果,所以可以用dp解决一块木板最多能正确粉刷的格子数。
用f[i][j][l]表示这块木板从左到右刷了i次,刷完了第j块,且最后一次粉刷的颜色是l,那么很容易想到转移方程:
f[i][j][l] = max(f[i][j][l],f[i-1][k][l^1]+第k+1块到第j块颜色为l的格子数)(0<=k<j)
注意第k+1块到第j块颜色为l的格子数可以提前预处理用一个数组记录下来
然后把这块木板刷k次最多能正确粉刷的格子数用一个vector保存下来
知道了一块木板刷k次最多能正确粉刷多少格子后就很容易能想到i块木板刷j次的最多正确粉刷格子数的dp解法
dp[i][j]表示前i块木板刷了j次的最多正确粉刷格子数,那么可以很容易得到状态转移方程:
dp[i][j] = max(dp[i-1][j-k]+第i块木板刷k次最多能正确粉刷的格子数)(0<=k<=木板的连续块数)
最后统计一下答案就出来了
代码
#include<bits/stdc++.h>
using namespace std;
int n,m,t,dp[55][2505],ans;
string s[55];
vector<int>v[55];//保存每一块木板粉刷k次能正确多少
void preProcess(int x)//预处理每一块木板粉刷k次能正确多少
{
vector<int>y,z;
int now = s[x][0]-'0',sum = 0,f[55][55][2] = {0},p[55][55][2] = {0},maxn = 0;
y.push_back(0);//因为vector下标从0开始,所以加一个0代表刷0次正确0块,
z.push_back(0);//z代表每一块的颜色是什么,方便后面统计第i到j块颜色为l的格子数
//将木板连续相同颜色格子合并
for(int i = 0;i<m;i++)
{
if(now==0&&s[x][i]=='0'||now==1&&s[x][i]=='1')
sum++;
if(now==0&&s[x][i]=='1'&&sum!=0)
y.push_back(sum),z.push_back(0),now = 1,sum = 1;
if(now==1&&s[x][i]=='0'&&sum!=0)
y.push_back(sum),z.push_back(1),now = 0,sum = 1;
}
if(sum!=0)
y.push_back(sum),z.push_back(s[x][m-1]-'0');
sum = y.size();
//预处理第i到j块颜色为l的格子数
for(int i = 0;i<sum;i++)
for(int j = i;j<sum;j++)
for(int k = i;k<=j;k++)
for(int l = 0;l<=1;l++)
p[i][j][l]+=y[k]*(z[k]==l);
//得到这块木板从左到右刷i次刷完第j块且最后一次粉刷颜色为l最多能有多少格子正确
for(int i = 1;i<sum;i++)
for(int j = 1;j<sum;j++)
for(int k = 0;k<j;k++)
for(int l = 0;l<=1;l++)
f[i][j][l] = max(f[i][j][l],f[i-1][k][l^1]+p[k+1][j][l]);
//统计保存这块木板结果到vector中
for(int i = 0;i<sum;i++,v[x].push_back(maxn),maxn = 0)
for(int j = 0;j<sum;j++)
maxn = max(maxn,max(f[i][j][0],f[i][j][1]));
}
int main()
{
cin>>n>>m>>t;
for(int i = 1;i<=n;i++)
cin>>s[i],preProcess(i);
//得到刷完第i块木板共刷了j次且最多能有多少格子正确
for(int i = 1;i<=n;i++)
for(int j = 1;j<=t;j++)
for(int k = 0;k<v[i].size()&&k<=j;k++)
dp[i][j] = max(dp[i][j],dp[i-1][j-k]+v[i][k]);
for(int i = 1;i<=n;i++)
ans = max(ans,dp[i][t]);
cout<<ans;
return 0;
}
欢迎关注微信公众号:Java后台开发
致力于分享原创计算机与软件开发知识及SSM、Spring cloud、Redis、微服务等Java后端开发技术
公众号里还有很多开发工具及学习资料