P4158 [SCOI2009]粉刷匠
P4158 [SCOI2009]粉刷匠
大致题意
给\(n\)行长度为\(m\)的字符串,每个字符都要被粉刷成黑色或白色,一共有\(t\)次粉刷机会,每次粉刷,只能选择一行字符串中一段连续的子串,然后涂上一种颜色,每个格子最多只能被粉刷一次。
求最多刷对格子总数
\(n,m≤50,t≤2500\)
分析
简单的线性\(dp\)
算法1
设\(f(i,j,k,0/1)\)表示粉刷前\(i\)行第\(i\)行粉刷前\(j\)行共用了\(k\)次操作,前一个刷对\(/\)错时能刷对的最多格子数
分类讨论每一种情况,转移方程:
\(f(i,j,k,1) = \begin{cases}max(f(i,j-1,k-1,0)+1,f(i,j-1,k,1)+1)&a_j=a_{j-1}\\max(f(i,j-1,k,0)+1,f(i,j-1,k-1,1)+1)&a_j≠a_{j-1}\end{cases}\)
\(f(i,j,k,0) = \begin{cases}max(f(i,j-1,k,0)+1,f(i,j-1,k-1,1)+1)&a_j=a_{j-1}\\f(i,j-1,k,1)&a_j≠a_{j-1}\end{cases}\)
算法2
观察发现,不可能在一段连续相同的字符中出现"转折点",因此我们可以把相同的一段字符合并起来
设\(val(i,j)\)表示第\(i\)行第\(j\)段连续字符的长度,转移方程:
\(f(i,j,k,0) = max(f(i,j-1,k,1) , f(i,j-1,k-1,0))\)
\(f(i,j,k,1) = max(f(i,j-1,k,0) , f(i,j-1,k-1,1)) + val(i,j)\)
\(code:\)
/*if(j==1){
f[i][j][k][1] = max(f[i-1][m][k-1][0]+1,f[i-1][m][k-1][1]+1);
f[i][j][k][0] = max(f[i-1][m][k-1][0],f[i-1][m][k-1][1]);
continue;
}
if(s[j]==s[j-1]){
f[i][j][k][1] = max(f[i][j-1][k-1][0]+1,f[i][j-1][k][1]+1);
f[i][j][k][0] = max(f[i][j-1][k][0],f[i][j-1][k-1][1]);
}
else{
f[i][j][k][1] = max(f[i][j-1][k][0]+1,f[i][j-1][k-1][1]+1);
f[i][j][k][0] = f[i][j-1][k][1];
}算法1*/
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2510,MAXM = 60;
int f[MAXM][MAXM][MAXN][2];
char s[MAXN];
int n,m,t;
int tot[MAXM];
int val[MAXM][MAXN];
int a[MAXM][MAXN];
int dp[MAXM][MAXN];
int ans = 0;
int main(){
scanf("%d%d%d",&n,&m,&t);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
for(int j=1;j<=m;j++){
if(s[j]!=s[j-1]) ++tot[i];
val[i][tot[i]]++;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=tot[i];j++){
for(int k=1;k<=t;k++){
if(j==1){//从上一行转移
f[i][j][k][0] = max(f[i-1][tot[i-1]][k-1][0],f[i-1][tot[i-1]][k-1][1]);
f[i][j][k][1] = max(f[i-1][tot[i-1]][k-1][0],f[i-1][tot[i-1]][k-1][1])+val[i][j];
}
else{
f[i][j][k][0] = max(f[i][j-1][k][1] , f[i][j-1][k-1][0]);
f[i][j][k][1] = max(f[i][j-1][k][0] , f[i][j-1][k-1][1]) + val[i][j];
}
}
}
}
ans = max(f[n][tot[n]][t][1],f[n][tot[n]][t][0]);
cout<<ans;
}

浙公网安备 33010602011771号