[SCOI2009]粉刷匠
题目描述:
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 .
输出描述:
输出包含一个整数,最多能正确粉刷的格子数
思路:
一看数据范围, 肯定是个 dp 了
但是这个数据范围做不了状压 dp
考虑普通的 dp
这个问题的难点在于他是二维的情况(多个木块)
那么如果是一维的情况怎么做呢?我们先考虑一下
令
f
[
i
]
[
j
]
[
k
]
f[i][j][k]
f[i][j][k] 表示第 i 个木板粉刷 j次涂了前面 k 个格子的正确格子数
因为只有两种颜色, 对于某一块木板, 我们可以维护一个前缀和
s
u
m
[
i
]
[
j
]
sum[i][j]
sum[i][j] 表示第
i
i
i 个木板的第
j
j
j 个格子前蓝色格子的数目
对于单个木板有
f
[
i
]
[
j
]
[
k
]
=
m
a
x
(
f
[
i
]
[
j
]
[
k
]
,
f
[
i
]
[
j
−
1
]
[
l
]
+
m
a
x
(
s
u
m
[
i
]
[
k
]
−
s
u
m
[
i
]
[
l
]
,
k
−
l
−
s
u
m
[
i
]
[
k
]
+
s
u
m
[
i
]
[
l
]
)
f[i][j][k] = max(f[i][j][k], f[i][j - 1][l] + max(sum[i][k] - sum[i][l], k - l - sum[i][k] + sum[i][l])
f[i][j][k]=max(f[i][j][k],f[i][j−1][l]+max(sum[i][k]−sum[i][l],k−l−sum[i][k]+sum[i][l])
表示对第
i
i
i 块木板涂了
j
j
j 次, 此时到第
k
k
k 个位置可以从第
i
i
i 块木板的涂了
j
−
1
j - 1
j−1次的第
l
l
l 个位置转移过来
对于要么取蓝色, 要么取红色, 我们取个 max 即可,
s
u
m
[
i
]
[
k
]
−
s
u
m
[
i
]
[
l
]
是
[
l
,
k
]
的
蓝
色
格
子
k
−
l
−
s
u
m
[
i
]
[
k
]
+
s
u
m
[
i
]
[
l
]
sum[i][k] - sum[i][l]是 [l, k] 的蓝色格子 k - l - sum[i][k] + sum[i][l]
sum[i][k]−sum[i][l]是[l,k]的蓝色格子k−l−sum[i][k]+sum[i][l] 是
[
l
,
k
]
[l, k]
[l,k] 的红色格子
此时再考虑多个木板情况
显然有
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
]
,
d
p
[
i
−
1
]
[
j
−
k
]
+
f
[
i
]
[
k
]
[
m
]
)
dp[i][j] = max(dp[i][j], dp[i - 1][j - k] + f[i][k][m])
dp[i][j]=max(dp[i][j],dp[i−1][j−k]+f[i][k][m])
表示到第 i 个木板涂了 j 次可以从 第i - 1个木板涂了 j - k 次转移过来
Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 55;
int sum[N][N];
int f[N][N][N]; //对于第i块板,粉刷j次前k个格子得到的最多正确粉刷格子
int dp[N][N*50]; //前i块板子,粉刷j次获得的最多正确粉刷格子
int main()
{
int n,m,t;
cin>>n>>m>>t;
string s;
for(int i=1;i<=n;i++){
cin>>s;
for(int j=0;j<m;j++)
sum[i][j+1] = sum[i][j] + (s[j]-'0');
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=m;k++)
for(int l=j-1;l<k;l++)
f[i][j][k] = max(f[i][j][k],f[i][j-1][l]+max(sum[i][k]-sum[i][l],k-l-sum[i][k]+sum[i][l]));
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=t;j++)
for(int k=0;k<=min(j,m);k++){
dp[i][j] = max(dp[i][j],dp[i-1][j-k]+f[i][k][m]);
ans = max(dp[i][j],ans);
}
cout<<ans<<"\n";
return 0;
}
参考博客:https://blog.nowcoder.net/n/24993fefbc7e4c7f8b439891af77d701