题目链接

题目

题目描述

windy有 N 条木板需要被粉刷。 每条木板被分为 M 个格子。 每个格子要被刷成红色或蓝色。

windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色。 每个格子最多只能被粉刷一次。

如果windy只能粉刷 T 次,他最多能正确粉刷多少格子?

一个格子如果未被粉刷或者被粉刷错颜色,就算错误粉刷。

输入描述

输入文件paint.in第一行包含三个整数,N M T。

接下来有N行,每行一个长度为M的字符串,'0'表示红色,'1'表示蓝色。

输出描述

输出文件paint.out包含一个整数,最多能正确粉刷的格子数。

示例1

输入

3 6 3
111111
000000
001100

输出

16

备注

30%的数据,满足 \(1 \le N,M \le 10 ;0 \le T \le 100\) 。

100%的数据,满足 \(1 \le N,M \le 50 ; 0 \le T \le 2500\) 。

题解

方法一

知识点:线性dp。

这道题相当于 \(k\) 串最大和套 \(k\) 串最大和,十分巧妙。

首先考虑设 \(f[i][j]\) 为考虑到第 \(i\) 行,共连续刷了 \(j\) 次的最多刷对格数。发现转移时的累加和变为第 \(i\) 行,考虑到第 \(m\) 格,共刷了 \(k\) 次的最多刷对格数,用 \(g[i][m][k]\) 表示。有转移方程为:

\[f[i][j] = \max (f[i-1][j],f[i-1][j-k] + g[i][m][k]),1 \leq k \leq j
\]

接下来考虑求 \(g[i][j][k]\) ,每行是独立的,第一维可以不考虑。接下来就和 \(k\) 串最大和一致,有转移方程:

\[g[i][j][k] = \max (g[i][j-1][k],g[i][j-l][k-1] + \max(s,l - s)),s = sum[i][j] - sum[i][j-l] ,1 \leq l \leq j
\]

其中 \(sum[i][j]\) 代表第 \(i\) 行前 \(j\) 个数里 \(1\) 的数量,\(\max (s,l-s)\) 表示新刷的 \([j-l+1,j]\) 里刷数量最多的种类。

时间复杂度 \(O(nm^2t +nt^2)\)

空间复杂度 \(O(nmt)\)

方法二

知识点:线性dp。

思路差不多,就是状态设置不一样,设 \(f[i][j][k][l]\) 表示为在 \((i,j)\) 处的格子,已经刷了 \(k\) 次,这个格子状态为 \(l\) (0/1,涂0/涂1)。显然有转移方程:

\[\left \{
\begin{array}{l}
f[i][j][k][0] = \max(f[i][j-1][k-1][0],f[i][j-1][k-1][1]) + [a[i][j] = 0] &,j = 1\\
f[i][j][k][1] = \max(f[i][j-1][k-1][0],f[i][j-1][k-1][0]) + [a[i][j] = 1] &,j = 1\\
f[i][j][k][0] = \max(f[i][j-1][k][0],f[i][j-1][k-1][1]) + [a[i][j] = 0] &,j \neq 1\\
f[i][j][k][1] = \max(f[i][j-1][k-1][0],f[i][j-1][k][0]) + [a[i][j] = 1] &,j \neq 1
\end{array}
\right.
\]

\(j = 1\) ,必须换行,即必须分段。

时间复杂度 \(O(nmt)\)

空间复杂度 \(O(nmt)\)

代码

方法一

#include <bits/stdc++.h>

using namespace std;

int sum[57][57], f[57][2507], g[57][57][2507];

int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m, t;
cin >> n >> m >> t;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
char x;
cin >> x;
sum[i][j] = sum[i][j - 1];
if (x == '1') sum[i][j]++;
}
}
for (int i = 1;i <= n;i++)
for (int j = 1;j <= m;j++)
for (int k = 1;k <= t;k++)
for (int l = 1;l <= j;l++)
g[i][j][k] = max(g[i][j][k], g[i][j - l][k - 1] + max(sum[i][j] - sum[i][j - l], l - (sum[i][j] - sum[i][j - l]))); for (int i = 1;i <= n;i++)
for (int j = 1;j <= t;j++)
for (int k = 1;k <= j;k++)
f[i][j] = max(f[i][j], f[i - 1][j - k] + g[i][m][k]); cout << f[n][t] << '\n';
return 0;
}

方法二

#include <bits/stdc++.h>

using namespace std;

int a[57][57], f[57][57][2507][2];

int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n, m, t;
cin >> n >> m >> t;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
char x;
cin >> x;
a[i][j] = x - '0';
}
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
for (int k = 1;k <= t;k++) {
if (j == 1) {
f[i][j][k][0] = max(f[i - 1][m][k - 1][1], f[i - 1][m][k - 1][0]) + (a[i][j] == 0);
f[i][j][k][1] = max(f[i - 1][m][k - 1][1], f[i - 1][m][k - 1][0]) + (a[i][j] == 1);
}
else {
f[i][j][k][0] = max(f[i][j - 1][k][0], f[i][j - 1][k - 1][1]) + (a[i][j] == 0);
f[i][j][k][1] = max(f[i][j - 1][k - 1][0], f[i][j - 1][k][1]) + (a[i][j] == 1);
}
}
}
}
cout << max(f[n][m][t][0], f[n][m][t][1]) << '\n';
return 0;
}

NC20273 [SCOI2009]粉刷匠的更多相关文章

  1. BZOJ 1296: [SCOI2009]粉刷匠 分组DP

    1296: [SCOI2009]粉刷匠 Description windy有 N 条木板需要被粉刷. 每条木板被分为 M 个格子. 每个格子要被刷成红色或蓝色. windy每次粉刷,只能选择一条木板上 ...

  2. BZOJ 1296: [SCOI2009]粉刷匠( dp )

    dp[ i ][ j ] = max( dp[ i - 1 ][ k ] + w[ i ][ j - k ] )  ( 0 <= k <= j ) 表示前 i 行用了 j 次粉刷的机会能正 ...

  3. 【BZOJ1296】[SCOI2009]粉刷匠(动态规划)

    [BZOJ1296][SCOI2009]粉刷匠(动态规划) 题面 BZOJ 洛谷 题解 一眼题吧. 对于每个串做一次\(dp\),求出这个串刷若干次次能够达到的最大值,然后背包合并所有的结果即可. # ...

  4. 1296: [SCOI2009]粉刷匠[多重dp]

    1296: [SCOI2009]粉刷匠 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1919  Solved: 1099[Submit][Statu ...

  5. 【BZOJ1296】[SCOI2009]粉刷匠 (DP+背包)

    [SCOI2009]粉刷匠 题目描述 \(windy\)有 \(N\) 条木板需要被粉刷. 每条木板被分为 \(M\) 个格子. 每个格子要被刷成红色或蓝色. \(windy\)每次粉刷,只能选择一条 ...

  6. 背包 DP【洛谷P4158】 [SCOI2009]粉刷匠

    P4158 [SCOI2009]粉刷匠 windy有 N 条木板需要被粉刷. 每条木板被分为 M 个格子. 每个格子要被刷成红色或蓝色. windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上 ...

  7. BZOJ_1296_[SCOI2009]粉刷匠_DP

    BZOJ_1296_[SCOI2009]粉刷匠_DP Description windy有 N 条木板需要被粉刷. 每条木板被分为 M 个格子. 每个格子要被刷成红色或蓝色. windy每次粉刷,只能 ...

  8. [Bzoj1296][Scoi2009] 粉刷匠 [DP + 分组背包]

    1296: [SCOI2009]粉刷匠 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 2184  Solved: 1259[Submit][Statu ...

  9. bzoj1296: [SCOI2009]粉刷匠(DP)

    1296: [SCOI2009]粉刷匠 题目:传送门 题解: DP新姿势:dp套dp 我们先单独处理每个串,然后再放到全局更新: f[i][k]表示当前串枚举到第i个位置,用了k次机会 F[i][j] ...

随机推荐

  1. Golang 常见设计模式之单例模式

    之前我们已经看过了 Golang 常见设计模式中的装饰和选项模式,今天要看的是 Golang 设计模式里最简单的单例模式.单例模式的作用是确保无论对象被实例化多少次,全局都只有一个实例存在.根据这一特 ...

  2. BSP-充电名词解释

    充电名词解释 A~G H~N O~T U~Z A~G ACA = accessory charger adapter = 辅助充电适配器 ACC = apparent charge capacity ...

  3. 管理订单状态,该上状态机吗?轻量级状态机COLA StateMachine保姆级入门教程

    前言 在平常的后端项目开发中,状态机模式的使用其实没有大家想象中那么常见,笔者之前由于不在电商领域工作,很少在业务代码中用状态机来管理各种状态,一般都是手动get/set状态值.去年笔者进入了电商领域 ...

  4. 喜提JDK的BUG一枚!多线程的情况下请谨慎使用这个类的stream遍历。

    你好呀,我是歪歪. 前段时间在 RocketMQ 的 ISSUE 里面冲浪的时候,看到一个 pr,虽说是在 RocketMQ 的地盘上发现的,但是这个玩意吧,其实和 RocketMQ 没有任何关系. ...

  5. Java常用类-包装类

    包装类 ​ Java中的基本类型功能简单,不具备对象的特性,为了使基本类型具备对象的特性,所以出现了包装类,就可以像操作对象一样操作基本类型数据;包装类不是为了取代基本数据类型,而是在数据类型需要使用 ...

  6. 『现学现忘』Git后悔药 — 30、版本回退git reset --hard命令说明

    git reset --hardcommit-id命令:回退到指定版本.(hard:强硬,严格的回退) 该命令不仅移动了分支中HEAD指针的位置,还将工作区和暂存区中数据也回退到了指定的版本. (提示 ...

  7. 强化学习-学习笔记9 | Multi-Step-TD-Target

    这篇笔记依然属于TD算法的范畴.Multi-Step-TD-Target 是对 TD算法的改进. 9. Multi-Step-TD-Target 9.1 Review Sarsa & Q-Le ...

  8. Tapdata Cloud 2.1.5来啦:新增支持Amazon RDS数据库,错误日志查询更便捷,Agent部署细节再优化

      需求持续更新,优化一刻不停--Tapdata Cloud 2.1.5 来啦!   最新发布的版本中,数据连接再上新,同时新增任务报错相关信息快速查询入口,开始支持 JVM 参数自定义设置.   更 ...

  9. 方法引用(Method References)

    * 方法引用的使用 * * 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! * * 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口 ...

  10. 字节输入流_InputStream类&FileInputStream类介绍和字节输入流读取字节数据

    java.io.InputStream:字节输入流 此抽象类是表示字节输入流的所有类的超类 定义了所有子类共性的方法: int read()从输入流中读取数据的下一个字节 int read(byte[ ...