[BZOJ1296][SCOI2009]粉刷匠(DP)

本文介绍了一种使用两次动态规划解决二维涂色问题的方法,首先预处理出每行0和1的数量,再通过两阶段DP求解最大正确涂色数量,最终得到问题的最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先预处理出sum0[i][j]sum1[i][j],分别表示第i行的前j个格子内0的个数和1的个数。
进行第一次DP,f[i][j][k]表示第i行到了第j个格子粉刷了k次,最多能粉刷正确的格子数。注意这里k的上界是m而不是T
转移就是,如果(i,j)被粉刷到,那么就枚举第k次粉刷的开始位置;如果(i,j)没有被粉刷到,那么就枚举第k次粉刷的结束位置:
f[i][j][k]=maxj1l=0(max(f[i][l][k],
f[i][l][k1]+sum0[i][j]sum0[i][l],
f[i][l][k1]+sum1[i][j]sum1[i][l]))
然后再进行第二次DP,g[i][j]表示到了第i行粉刷了j次的最优解。此时转移为:
g[i][j]=maxmin(m,j)k=0(g[i1][jk]+f[i][m][k])
最后答案就是g[n][T]。复杂度O(nm3+nmT)
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 55, M = 3005;
int n, m, T, a[N][N], f[N][N][N], g[N][M], sum[N][N][2];
void chkmax(int &a, int b) {if (b > a) a = b;}
int main() {
    int i, j, k, l; scanf("%d%d%d", &n, &m, &T);
    for (i = 1; i <= n; i++) for (j = 1; j <= m; j++) {
        scanf("%1d", &a[i][j]);
        sum[i][j][a[i][j]] = sum[i][j - 1][a[i][j]] + 1;
        sum[i][j][a[i][j] ^ 1] = sum[i][j - 1][a[i][j] ^ 1];
    }
    for (i = 1; i <= n; i++) for (j = 1; j <= m; j++)
    for (k = 1; k <= m; k++) for (l = 0; l < j; l++) {
        chkmax(f[i][j][k], f[i][l][k]);
        chkmax(f[i][j][k], f[i][l][k - 1] + sum[i][j][0] - sum[i][l][0]);
        chkmax(f[i][j][k], f[i][l][k - 1] + sum[i][j][1] - sum[i][l][1]);
    }
    for (i = 1; i <= n; i++) for (j = 1; j <= T; j++)
    for (k = 0; k <= min(m, j); k++)
        chkmax(g[i][j], g[i - 1][j - k] + f[i][m][k]);
    cout << g[n][T] << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值