【题解】P4158 [SCOI2009]粉刷匠

是一道资源规划 DP 的好题,但是我想了很久还去看了题解。/kk我真菜。


题目链接

P4158 [SCOI2009]粉刷匠 - 洛谷

题意概述

发现自己实在不会简化题意于是就抄了原题(雾)。

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

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

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

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

思路分析

这题我至少读错了有 1h 之久……

题目要求的是粉刷 \(T\) 次最多的正确格子数。

比较直接的思路是,定义 \(dp_{i,j}\) 表示前 \(i\) 行木板,粉刷 \(j\) 次最多的正确格子数。

那么对于第 \(i\) 行状态可以从第 \(i-1\) 行转移而来,显然这是一个 01 背包。

那么有:

\[dp_{i,j}=\max \limits_{1 \le k \le j}(dp_{i-1,k}+cnt_{i,k}) \]

其中 \(cnt_{i,k}\) 表示的是第 \(i\) 行粉刷 \(k\) 次最多的正确格子数。

显然这个 \(cnt\) 可以对每一行分别求。那么我们也对每一行分别考虑。

对于第 \(i\) 行:

设 \(g_{j,k}\) 表示前 \(j\) 个位置粉刷 \(j\) 次最多的正确格子数。

考虑如何转移。

比较显然的是,对于前 \(j\) 个位置,肯定是由:连续一段 1,连续一段 0,连续一段 1……这样 01 的连续段相互交错形成的,那么我们可以将这些连续的段切成几部分得到 \(g_{j,k}\) 的转移。

可以考虑利用类似区间 DP 的思想,枚举断点 \(l\),将前 \(j\) 个位置划分为前 \(l\) 个位置和区间 \([l+1,j]\) 两段,其中 \([l+1,j]\) 作为最后一次涂色的区间。

那么 \(g_{j,k}\) 就可以由 \(g_{l,k-1}\) 转移得到,即:

\[g_{j,k}=\max \limits_{0 \le l < j}(g_{l,k-1}+mx_{l+1,j}) \]

(注意 \(l\) 的范围)

其中 \(mx_{l,r}\) 表示的是区间 \([l,r]\) 中数量最多的颜色的数量。

\(mx_{l,r}\) 可以直接暴力枚举得到。

那么最后的答案就是 \(dp_{n,t}\)。

梳理一下求解过程:

求粉刷 \(T\) 次最多的正确格子数 \(\rightarrow\) 求 \(dp_{i,j}\) \(\rightarrow\) 求 \(g_{j,k}\)(将求整个矩形转化为一行)\(\rightarrow\) 求 \(mx_{l+1,j}\)(将 \(k\) 次转化为 1 次)。

求解步骤

对于每一行 \(i\):

  1. 暴力枚举求出 \(mx_{l,r}\):\(1-m\) 所有区间。

  2. 求 \(g_{j,k}\);

  3. 求 \(dp_{i,j}\)。

易错点

  1. 每次枚举一个新的 \(i\) 都要将 \(g\) 数组和 \(mx\) 数组清空;

  2. 在求 \(g_{j,k}\) 时枚举的断点 \(l\) 范围是 \([0,j)\);

  3. \(dp_{n,t}\) 第二维由于 \(t\) 的范围是 2500,所以要开 \(>2500\);

一些思考

这道题我刚开始读错了好久的题意,并在读错的题意上思考了好久但是并没有想出来。

但是我总觉得我读错的那个题意也可以作为一道题目,并且一定有正确的解法。(盲目自信)

我刚开始没有看到题目中“每个格子只能被粉刷一次”这句话。

然后感觉可以先考虑将每一行所有的格子涂色正确的情况整出来。也就是,将第 \(i\) 行涂正确至少需要多少次。

那么发现发现对于单个的一行,实际上就是P4170 [CQOI2007]涂色 - 洛谷。

那么可以用 \(dp_{i,l,r}\) 表示第 \(i\) 行区间 \([l,r]\) 涂正确的最少次数,然后做区间 DP。

然后感觉是个有点类似于贪心和 01 背包的东西,但一直没想出解法。

虽然说我看错题目很傻逼,也没有想到看错了的题意的正确解法(我太菜了),但是我还是把它记录了下来,希望以后可以有所突破。或者,看到这篇文章的你如果有所想法,可以在下方留言或者联系我说出你的解法。

毕竟探索本身,就是一件很有意义的事情嘛。

实现代码

//luoguP4158
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=55;
const int maxt=maxn*maxn;
int a[maxn][maxn],dp[maxn][maxt],g[maxn][maxn],mx[maxn][maxn];inline int read()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}return x*f;
}int main()
{int n,m,t;n=read();m=read();t=read();for(int i=1;i<=n;i++){string s;cin>>s;for(int j=0;j<m;j++){if(s[j]=='1')a[i][j+1]=1;//bug:j 写成 i…… else a[i][j+1]=0;}   }
//  for(int i=1;i<=n;i++)
//  {
//      for(int j=1;j<=m;j++)cout<<a[i][j]<<" ";
//      cout<<endl;
//  }for(int i=1;i<=n;i++){memset(g,0,sizeof(g));memset(mx,0,sizeof(mx));int cnt1=0,cnt0=0;for(int l=1;l<=m;l++){if(a[i][l]==0)cnt0=1,cnt1=0;else cnt1=1,cnt0=0;mx[l][l]=1;for(int r=l+1;r<=m;r++){if(a[i][r]==0)cnt0++;else cnt1++;mx[l][r]=(cnt0>cnt1)?cnt0:cnt1;
//              cout<<cnt0<<" "<<cnt1<<endl;
//              cout<<i<<" "<<l<<" "<<r<<" "<<mx[l][r]<<endl;}}for(int j=1;j<=m;j++){for(int k=1;k<=j;k++){for(int l=0;l<=j;l++)//bug: j 的范围:[0,j) {
//                  cout<<l<<" "<<" "<<g[l][k-1]<<" "<<mx[l+1][j]<<endl; g[j][k]=max(g[j][k],g[l][k-1]+mx[l+1][j]);//注意这里没有等于即 l!=k 且 l 需要从 0 开始枚举 }
//              cout<<i<<" "<<j<<" "<<k<<" "<<g[j][k]<<endl;}}for(int j=t;j>=1;j--){for(int k=1;k<=j;k++)dp[i][j]=max(dp[i][j],dp[i-1][j-k]+g[m][k]);
//          cout<<i<<" "<<j<<" "<<dp[i][j]<<endl;}}cout<<dp[n][t]<<endl;return 0;
}
/*mx[l][r] 表示的是对于一块木板的区间 [l,r] 这一段最多的颜色的数量。
g[i][j] 表示的是考虑了前 i 个位置,粉刷 j 次的最多正确粉刷的个数。
g[i][j]=max(g[i][j],g[l][j-1]+mx[l+1][i]).
dp[i][j] 表示考虑了前 i 行,粉刷 j 次的最多正确粉刷个数。
显然这是 01 背包。那么有:
dp[i][j]=max(dp[i][j],dp[i-1][j-k]+g[m][k]).
*/

感觉我还是好菜啊。做了那么多 DP 的题目,但是还是对有些题毫无感觉,甚至设计不出 DP 状态,也不知道我的 DP 什么时候才能有质的突破呢?

【题解】P4158 [SCOI2009]粉刷匠(DP,背包)相关推荐

  1. P4158 [SCOI2009]粉刷匠(dp)

    P4158 [SCOI2009]粉刷匠(dp) 考虑每行独立计算. 所以可以开一个三维数组:g[i][j][k]g[i][j][k]g[i][j][k]第iii行前jjj列涂了kkk次的最大值. 然后 ...

  2. [洛谷P4158][SCOI2009]粉刷匠(动态规划)

    [洛谷P4158][SCOI2009]粉刷匠(动态规划) 题目描述 输入描述 输出描述 示例 输入 输出 题目思路 代码 欢迎关注微信公众号:Java后台开发 题目描述 windy有 N 条木板需要被 ...

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

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

  4. [SCOI2009]粉刷匠 DP)

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

  5. [洛谷]P4158 [SCOI2009]粉刷匠 (#线性dp+背包dp)

    题目描述 windy有 N 条木板需要被粉刷. 每条木板被分为 M 个格子. 每个格子要被刷成红色或蓝色. windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色. 每个格子最多只能被 ...

  6. 【题解】洛谷P4158 [SCOI2009] 粉刷匠(DP)

    次元传送门:洛谷P4158 思路 f[i][j][k][0/1]表示在坐标为(i,j)的格子 已经涂了k次 (0是此格子涂错 1是此格子涂对)涂对的格子数 显然的是 每次换行都要增加一次次数 那么当j ...

  7. [SCOI2009]粉刷匠 dp

    原题链接 题解 : 这道题我感觉很新颖的地方是 这道题是二维的 , 所以要进行两次dp处理 , 令 f[i][j][k] 表示第 i个木板粉刷 j 次涂了前面 k 个格子的正确格子数 因为只有两种颜色 ...

  8. P4158[SCOI2009]粉刷匠

    题目描述 windy有 N 条木板需要被粉刷. 每条木板被分为 M 个格子. 每个格子要被刷成红色或蓝色. windy每次粉刷,只能选择一条木板上一段连续的格子,然后涂上一种颜色. 每个格子最多只能被 ...

  9. bzoj 1296: [SCOI2009]粉刷匠(DP+DP)

    1296: [SCOI2009]粉刷匠 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 2339  Solved: 1348 [Submit][Sta ...

最新文章

  1. win10安装emacs+spacemacs,建议用官方安装方式
  2. python-正则表达式练习题
  3. spi 协议驱动设计
  4. Mysql数据库使用总结
  5. caffe linux 教程,Caffe 深度学习入门教程 - 安装配置Ubuntu14.04+CUDA7.5+Caffe+cuDNN_Linux教程_Linux公社-Linux系统门户网站...
  6. 小帅小胖智能机器人如何使用_小帅智能教育机器人使用技巧篇(下篇)
  7. mac服务器文件同步软件,Windows和Mac之间同步文件夹
  8. 旁站,子域名,C段的含义
  9. Oracle根据身份证号码判断性别,年龄
  10. 谷歌Chrome浏览器保存网页为PDF
  11. ARM 汇编指令 CPS CPSID CPSIE
  12. excel取消隐藏_excel批量取消工作表隐藏,困绕我们N年的问题终于有了答案!
  13. 有道云笔记怎么保存html文件,有道云笔记如何保存网页有道笔记保存页面教程...
  14. Neo4j图数据库高级应用系列 / 服务器扩展指南 APOC - apoc.periodic.iterate()过程在4.0版本中的重大变化
  15. 暴雪中国:《魔兽世界》团队正与国服地区新的潜在发行合作伙伴进行洽谈
  16. 3、Java 的变量和数据类型
  17. C语言六位数字钟程序,单片机制作的6位数字钟
  18. 技术一般的程序员找工作,如今真的一年比一年难...
  19. JVM调优专题-JVM调优参数
  20. 使用iso安装linux系统安装教程,史上最详细linux安装教程

热门文章

  1. CPU监控设置及数据获取方案
  2. PJSIP学习笔记15 -- PJSUA应用程序中的会议桥
  3. DM数据库网络通信异常排查
  4. 判断输入的日期是一年中第几天(调用函数)
  5. 2022GPLT团体程序设计天梯赛L1-083 谁能进图书馆
  6. QT Android 环境搭建(小白详细版)
  7. mysql salt bcrypt_BCrypt加密
  8. 情况紧急,网课没停妈先疯?这届老母亲实惨...
  9. Smarty之常用变量调节器
  10. linux中写crontab脚本,Linux中crond服务与crontab用法