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

首先预处理出 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值