YbtOJ 洛谷UVA10559 方块消除
题目大意:
n 个木块排成一列,每个木块有一个颜色,每次,你都可以点击一个木块,这样被点击的木块以及和它相邻并且同色的木块就会被消除。如果一次性消除了 k 个木块,就会得到 分,求最高得分。
算法分析:
首先,会想到贪心,但很容易发现有些最优解是消除了单个木块,让更长的相同颜色木块拼在一起。所以,贪心策略行不通。
于是,想到了区间DP。
做过版子题(石子合并)应该会很容易想到:
设 tot 表示原序列分成的颜色段的段数。
len[i] 表示第 i 个颜色段的长度。
color[i] 表示第 i 个颜色段的颜色。
dp[i][j] 表示消除第 i 段到第 j 段之间所有木块的最高得分。
然而,我们发现,如果单纯这么转移的话,当第 j 段后面仍然有和第 j 段前面的颜色段颜色相同的段,无法判断将两个颜色段合并是否更优。
所以,还需要再加一维。
设 dp[i][j][k] 表示在第 j 段后面出现 k 个与第 j 段颜色相同的木块,消除第 i 段到第 j 段之间的所有木块所能得到的最高得分。
转移方程
考虑转移对于 r 所在的颜色段,有两种情况,可以选择直接消除,或在 r 段前面补上与它颜色相同的木块。
什么意思?
首先,如果直接消除:将 r 段清除后,变成 r−1 段,k=0,再加上清除这一段的得分。
dp[l][r][k] = dp[l][r-1][0] + (len[r]+k) * (len[r]+k)
再考虑第二种情况,在 l 到 r 中找一段与 r 颜色相同的段,设为 x ,将这一段与 r 合并,也就是先清除中间的木块,再将合并的段清除。这时的 k 就变为 len[r]+k 。
dp[l][r][k] = max(dp[l][r][k],dp[x+1][r-1][0]+dp[l][x][len[r]+k])
代码(两种,记忆化搜索和动态规划)
先来看记忆化搜索的。
#include<bits/stdc++.h>
using namespace std;
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<<1)+(x<<3)+(ch^48) ; ch=getchar();}return x*f;
}
const int M = 210;
int dp[M][M][M],color[M],len[M],a[M]; //如上文的含义
int T,n,tot;
int dfs(int l,int r,int k){if(l==r)return (len[r]+k)*(len[r]+k); //如果搜到一起了,返回此时加的分数if(dp[l][r][k]) return dp[l][r][k]; //这就是记忆化,避免了重复搜索dp[l][r][k] = dfs(l,r-1,0) + (len[r]+k)*(len[r]+k);//上文的转移式1for(register int i(l) ; i<r-1 ; i=-~i){if(color[i] == color[r]){ //这两段颜色相同才能转移dp[l][r][k] = max(dp[l][r][k],dfs(i+1,r-1,0) + dfs(l,i,len[r]+k)); //转移式2}}return dp[l][r][k]; //最后返回此时能得到的最大得分
}
int main(){T=read();for(register int cnt(1) ; cnt<=T ; cnt=-~cnt){memset(dp,0,sizeof(dp));memset(color,0,sizeof(color));memset(len,0,sizeof(len));tot=0; //一定要清零,要不是你错都不知道怎么错的n=read();for(register int i(1) ; i<=n ; i=-~i) a[i] = read();for(register int i(1) ; i<=n ; i=-~i){if(a[i] != a[i-1]){ color[++tot] = a[i]; //这一部分先预处理颜色段数和颜色len[tot]=1;}else len[tot]++;}printf("Case %d: %d\n",cnt,dfs(1,tot,0)); //最后输出就行}return 0;
}
然后放上动态规划的(一定要注意转移顺序)
#include<bits/stdc++.h>
using namespace std;
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<<1)+(x<<3)+(ch^48) ; ch=getchar();}return x*f;
}
const int M = 210;
int dp[M][M][M],color[M],len[M],a[M],s[M];
int T,n,tot;
int main(){T=read();for(register int cnt(1) ; cnt<=T ; cnt=-~cnt){memset(dp,0,sizeof(dp));memset(color,0,sizeof(color));memset(len,0,sizeof(len));tot=0;n=read();for(register int i(1) ; i<=n ; i=-~i) a[i] = read();for(register int i(1) ; i<=n ; i=-~i){if(a[i] != a[i-1]){ color[++tot] = a[i];len[tot]=1;}else len[tot]++;}for(register int i(1) ; i<=n ; i=-~i) s[i] = s[i-1] + len[i]; //前缀和处理,预处理出前面最多有多少段颜色相同的for(register int l(0) ; l<=n ; l=-~l){for(register int i(1) ; i+l<=n ; i=-~i){ // 注意转移顺序int j = l+i;for(register int k(0) ; k<=s[n]-s[j-1] ; k=-~k){ //k循环到这就行了,一点点优化dp[i][j][k] = dp[i][j-1][0] + (len[j]+k)*(len[j]+k); //转移式1}for(register int k(i) ; k<j ; k=-~k){for(register int h(0) ; h<=s[n]-s[j-1] ; h=-~h){if(color[k] == color[j]){dp[i][j][h] = max(dp[i][j][h],dp[k+1][j-1][0] + dp[i][k][len[j]+h]); //转移式2}}}}}printf("Case %d: %d\n",cnt,dp[1][tot][0]);//颜色段只有tot,不能到n}return 0;
}
YbtOJ 洛谷UVA10559 方块消除相关推荐
- YBTOJ洛谷P4074:糖果公园(树上莫队)
文章目录 解析 update: 代码 所谓树上莫队,就是在树上的莫队 (逃) 传送门 解析 似乎就是树上的这道题 考虑如何转化为序列问题呢? 考虑dfs序 但是又一个问题... 似乎这条链的dfs序不 ...
- YBTOJ洛谷P4551:最长异或路径(trie树)
洛谷传送门 文章目录 题目描述 解析 代码 题目描述 解析 本题关键就在于一点: 若把每个点的深度dep[i]定义为从根到节点边权的异或和 那么i到j的路径异或和可以表示为: dep[i] ^ dep ...
- YBTOJ洛谷P3231:消毒(二分图匹配)
文章目录 题目描述 解析 代码 题目描述 最近在生物实验室工作的小 T 遇到了大麻烦. 由于实验室最近升级的缘故,他的分格实验皿是一个长方体,其尺寸为 a∗b∗ca*b*ca∗b∗c.为了实验的方便, ...
- ybtoj洛谷P4406三角形面积并(扫描线)
解析 暴力求出所有三角形之间的所有交点,提出所有的横坐标. 然后任意两个相邻的横坐标之间的面积都是若干个梯形. 那么就可以求出对于每一个横坐标截得的三角形长度的并的和,然后加在一起乘高除以二即可. 在 ...
- ybtoj洛谷P3268:圆的异或并(扫描线)
解析 很神奇的一道题. 关键条件:任意两个圆无交. 把一个圆分成上下两个圆弧,那么所有圆弧的高度关系不会发生变化. 所以可以开一个 set,维护一个从左往右扫的扫描线,按照当前扫描线的横坐标定义比较符 ...
- YBTOJ洛谷P4869:出现位置(线性基)
解析 关键结论: 若 nnn 个数组成的线性基大小为 SSS,则其子集异或组成的结果有 2S2^S2S 种,且每种结果都有 2n−S2^{n-S}2n−S 种方案. 证明:考虑 n−Sn-Sn−S 个 ...
- YBTOJ洛谷P2387: 魔法森林(LCT)
解析 LCT从板子到算法的入门题吧 有一些不知道的很实用的技巧 把边按a排序从小到大加入边 那么我们只需要维护当前1-n路径上的b的最小值即可 如果这条边两端点本来不连通,就直接link 否则找到路径 ...
- YBTOJ洛谷P3292:幸运数字(线性基、点分治/倍增)
解析 虽然使用三个log的倍增算法艹过去了 但是我们还是来聊聊正解吧 考虑点分治 对于当前的根,dfs求出联通块内每个点到当前根的线性基 一条路径的答案应该在路径出现上第一个成为根的点时统计到 具体来 ...
- YBTOJ洛谷P2839:最大中位数(主席树、二分答案)
遇事不决,二分试试 解析 很好的一道题 真是把主席树玩明白了 一个关于中位数的常用trick: 二分答案mid,把>=mid的看成1,<mid的看成-1,然后看最大子段和是否>=0 ...
最新文章
- 使用Docker搭建svn服务器教程
- [C++再学习系列] 函数模板和类模板
- css动画 animation
- 【错误记录】IntelliJ IDEA 中右键点击源码目录选择 New 选项 没有创建 Java Class 选项 ( 将对应的源码目录标记为 Sources 选项 )
- Tomcat启动过程中找不到JAVA_HOME解决方法
- 洛谷P4145 上帝造题的⑦minutes ②
- 前端技术分享:盒模型的概念和文本溢出解决办法
- 相册权限_手机相册太乱?1分钟教你快速管理自己的照片,非常好用!
- 动态设置html字号,动态设置html的font-size值 (适配文字大小)
- Windows -- Qt不能进行调试 -- Unknown debugger type No Engine
- ArcGIS中创建数据要素模板,便捷数据采集
- 正则表达式修正符的学习
- Adobe产品序列号
- 714. 买卖股票的最佳时机含手续费-动态规划算法
- 征文 | 青出于蓝而胜于蓝 国货之光GBase
- 火灾自动报警系统下综合布线施工要素
- 明解C语言入门篇_第9章_字符串的基本知识
- C语言基本语法——循环篇(三种常见的循环)
- 浙大计算机学院app开发,App Inventor - 零基础Android移动应用开发
- CAD图纸可以进行哪些格式的转换呢?