21.2.21 T3 粉刷匠 log P4158 && SCOI 2009 总结
作者:互联网
题面
Description
“我是一个粉刷匠,粉刷本领强~~~”粉刷匠qjx 哼着小曲高兴地开始了一天的工
作,这天qjx 有 \(n\) 条木板需要被粉刷。每条木板被分成 \(m\) 个格子,每个格子要被刷
成红色或蓝色。qjx 每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种
颜色,已知每个格子最多只能被粉刷一次。
如果qjx 只能粉刷 \(t\) 次,她最多能正确粉刷多少格子。
注意,一个格子如果未被粉刷或被粉刷成错误颜色,就算粉刷错误。
Input
第一行三个数n,m,t;
接下来n 行,每行一个长度为m 的字符“0”表示红色,“1”表示蓝色。
Output
一个整数,最多能正确粉刷的格子数。
Sample Input
3 6 3
111111
000000
001100
Sample Output
数据范围与约定
10% 数据满足l≤n, m≤10。
100%数据满足l≤n, m≤50:0≤t≤2500。
题解
这道题把行和列分开看很明显是两个很简单的DP题,但是DP是我学的最烂的地方,所以考场上啥也没写出来。。。
首先把行分开看,设dp[i][j][0]表示前i列,涂了j次,且当前,也就是第i列涂的是红色是正确的个数,dp[i][j][1]则表示当前涂的是蓝色时正确的个数。
把数组设好后,转移方程自然就很好想。首先是把行分隔开来看的dp,当枚举到某行第i列时,木板有两种情况,红和蓝,而我们的dp也有两种情况,涂与不涂,如果涂了之后,涂的颜色是正确的,那么转移之后正确的个数自然要 + 1。由此可以写出状态转移方程。
if(a[k][i] == 1) {
dp[i][j][0] = max(dp[i - 1][j][0],dp[i - 1][j - 1][1]);
dp[i][j][1] = max(dp[i - 1][j][1],dp[i - 1][j - 1][0]) + 1;
}
else {
dp[i][j][0] = max(dp[i - 1][j][0],dp[i - 1][j - 1][1]) + 1;
dp[i][j][1] = max(dp[i - 1][j][1],dp[i - 1][j - 1][0]);
}
通过这个,我们可以把每行刷j次的正确个数求出来,那这个东西有什么作用呢?开头说过,这道题可以把行与列分开看。那如果我们把每行看成是一个物品,那这道题是否就转换成了一个背包问题了呢?我们用另一个数组g[k][j]存下第k行刷j次的正确个数。那这个数组是否就可以看作是一个体积为j,价值为g[k][j]的物品了呢。那接下来的第二次dp就很好想了。我们用f[j]表示刷j次的正确个数(这个定义是建立在把每行看做是一个物品的基础上的),那么最后答案在f[j]中取最大值即可。
两次dp都要注意一个细节,因为每一格只能刷一次,所以当列数小于 \(t\) 的时候我们每一行最多只能刷 \(m\) 次。
完整代码如下
#include<cstdio>
#include<cstring>
const int N = 55;
const int T = 2505;
int dp[N][T][2],f[T],g[N][T],a[N][N],n,m,t,mzy,ans = 0;
inline int max(int a,int b) {
if(a > b) return a;
return b;
}
inline int min(int a,int b) {
if(a < b) return a;
return b;
}
void DP(int k) {
memset(dp,0,sizeof(dp));
for(int i = 1; i <= m; i++)
for(int j = 1; j <= mzy; j++) {
if(a[k][i] == 1) {
dp[i][j][0] = max(dp[i - 1][j][0],dp[i - 1][j - 1][1]);
dp[i][j][1] = max(dp[i - 1][j][1],dp[i - 1][j - 1][0]) + 1;
}
else {
dp[i][j][0] = max(dp[i - 1][j][0],dp[i - 1][j - 1][1]) + 1;
dp[i][j][1] = max(dp[i - 1][j][1],dp[i - 1][j - 1][0]);
}
}
for(int j = 1; j <= mzy; j++) g[k][j] = max(dp[m][j][0],dp[m][j][1]);
}
int main() {
scanf("%d%d%d",&n,&m,&t);
mzy = min(m,t);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++)
scanf("%1d",&a[i][j]);
DP(i);
}
for(int i = 1; i <= n; i++)
for(int j = t; j >= 0; j--) {
int yzm = min(j,mzy);
for(int k = 0; k <= yzm; k++)
f[j] = max(f[j],f[j - k] + g[i][k]);
}
for(int i = 0; i <= t; i++) ans = max(ans,f[i]);
printf("%d",ans);
return 0;
}
标签:log,格子,21.2,int,max,粉刷,2009,dp,正确 来源: https://www.cnblogs.com/sjzyh/p/14826253.html