Luogu P3886 [JLOI2009]神秘的生物 最小表示法,轮廓线DP,插头DP,动态规划
亲手写掉的第一道最小表示法!哈哈哈太开心啦~
不同于以往的几个插头\(dp\),这个题目的轮廓线是周围的一圈\(n\)个格子。而其所谓“插头”也变成了相邻格子的所属连通分量编号,并不是直接把前面几个题的思想往上套就可以轻松解决的了。这里我们就要采用一种叫最小表示法的东西来表示它的连通性信息啦~
(其实感觉是不是称之为逐格递推的轮廓线\(dp\)比较好。。。
而最小表示法是什么呢?举个例子,现在有这样一个序列\((5,5,3,2,4,1,3,2)\),序列中的每一个数代表第\(i\)个格子所属的连通分量。因为编号是人为设置的,所以其每一个编号也可以一一映射成等效的其他编号。为了连通性表示的规范和不重不漏,我们就把这个序列整理成意义等效的前提下字典序最小的形式,对这个序列就是\((1,1,2,3,4,5,2,3)\)。
整理的过程很简单,既然让字典序最小,那就尽可能让前面的比后面的小。从前向后遍历,如果当前这个编号第一次出现,就把它记录下来,换成新的编号\(tot+1\)。否则就直接将其改为已经记录下来的对应编号。
void min_express (int &zt) {
// zt 表示状态,tot 表示新整理的最大编号。
int tot = 0, id[N] = {0};
for (int i = 0; i < n; ++i) {
int now = get_wei (zt, i);
if (!now) continue;
if (id[now] != 0) {
zt = alt_wei (zt, i, id[now]);
} else {
zt = alt_wei (zt, i, id[now] = ++tot);
}
}
}
改完以后就可以考虑状态的转移了。这一点比较好考虑,都是插头\(dp\)的套路了。状态转移的讲解摘自洛谷日报——插头\(dp\)。(因为我太懒了\(QwQ\))
- 不选
当然得考虑状态合理,无下插头或下插头所在联通块还有其他插头,否则下插头被孤立而不形成联通块了
大家发现没有?
这里每个状态仅合理而已,并不能确定这是可取的最终状态,因为最后得保证只有一个联通块(其实上文也),详细请看 $update$
那为什么不考虑右插头呢?而要判断下插头呢?
因为下插头以后再也不会判断了,所以要考虑状态合理,右插头到下一行自然会考虑状态
右插头来自上一个转移状态,很可能形成新块 $7$ ,故这样转移条件会变得严苛,最终答案通常会小于正确答案
- 选
(1) !b1 and !b2 单独形成新块,此块命名为7
(2) b1 or b2 此块与插头相连,更新联通块状态,这就是伟大的最小表示法
一些解释:
\(Q\):为什么把新的连通块设置为7?
- \(A\):在最小表示法下,连通块标号从小到大记录。为了防止冲突,我们随手采用一个可以采用的里面最大的,到后面还会进行整理。
\(Q\):连上其他的分量应该怎么操作?
- \(A\):直接改就完事了\(=\_=\)。比如这个题里面,只要\(b1\)和\(b2\)有一个选中,你就可以连上其他分量,为了省一种讨论,我对目前这个格子采用了取两个分量较大值的方法,然后把其他编号等于两个分量编号较小值且不为\(0\)的块也改成这个较大分量的编号。
\(Code\):
#include <bits/stdc++.h>
using namespace std;
const int N = 9 + 5;
const int INF = 2e9;
const int base = 599999;
const int M = 600000 + 5;
void cmax (int &x, int y) {x = max (x, y);}
int ans = -INF;
int n, w[N][N];
int cur, las, cnt[2];
int nxt[M], head[M], dp[2][M], Hash[2][M];
int get_wei (int zt, int wei) {
return (zt >> (wei * 3)) % 8; // 8 进制状压
}
int alt_wei (int zt, int wei, int val) {
return zt - ((get_wei (zt, wei) - val) << (wei * 3));
}
int count (int zt, int val) {
int ret = -1;
for (int i = 0; i < n; ++i) {
ret += (get_wei (zt, i) == val);
}
return ret;
}
void min_express (int &zt) {
int tot = 0, id[N] = {0};
for (int i = 0; i < n; ++i) {
int now = get_wei (zt, i);
if (!now) continue;
if (id[now] != 0) {
zt = alt_wei (zt, i, id[now]);
} else {
zt = alt_wei (zt, i, id[now] = ++tot);
}
}
}
bool can_use (int zt) {
int tot0 = 0, tot1 = 0;
for (int i = 0; i < n; ++i) {
tot0 += get_wei (zt, i) == 0;
tot1 += get_wei (zt, i) == 1;
if (get_wei (zt, i) > 1) {
return false;
}
}
if (!tot1) return false;
return true;
}
void update (int zt, int val) {
min_express (zt);
if (can_use (zt)) {
ans = max (ans, val);
}
int _zt = zt % base;
for (int i = head[_zt]; i; i = nxt[i]) {
if (Hash[cur][i] == zt) {
cmax (dp[cur][i], val); return;
}
}
nxt[++cnt[cur]] = head[_zt];
head[_zt] = cnt[cur];
Hash[cur][cnt[cur]] = zt;
dp[cur][cnt[cur]] = val;
}
void print (int x, int y) {
cout << "r = " << x << " c = " << y << endl;
for (int i = 1; i <= cnt[cur]; ++i) {
cout << "zt = " << Hash[cur][i] << " ";
for (int j = 0; j < n; ++j) {
if (j != 0) cout << "_";
cout << get_wei (Hash[cur][i], j);
}
cout << " val = " << dp[cur][i] << endl;
}
}
int solve () {
update (0, 0);
// print (0, 0);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
las = cur, cur ^= 1, cnt[cur] = 0;
memset (head, 0, sizeof (head));
for (int k = 1; k <= cnt[las]; ++k) {
int zt = Hash[las][k];
int b1 = (j >= 2 ? get_wei (zt, j - 2) : 0);
int b2 = (j >= 1 ? get_wei (zt, j - 1) : 0);
int val = dp[las][k];
// 1. 不选当前格
if (!b2 || count (zt, b2)) {
// 没有插头 b2 或者 插头 b2 与轮廓线上其他的格子连通
update (alt_wei (zt, j - 1, 0), val); // 当前格子置为不选
}
if (!b1 && !b2) { // 新建连通分量
update (alt_wei (zt, j - 1, 7), val + w[i][j]);
}
if (b1 || b2) { // 连上其他的分量
int id = max (b1, b2), _zt = zt;
_zt = alt_wei (_zt, j - 1, id);
for (int i = 0; i < n; ++i) {
if (get_wei (_zt, i) == 0) continue;
if (get_wei (_zt, i) == min (b1, b2)) {
_zt = alt_wei (_zt, i, id);
}
}
update (_zt, val + w[i][j]);
}
}
// print (i, j);
}
}
return ans;
}
int main () {
// freopen ("data.in", "r", stdin);
cin >> n;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
cin >> w[i][j];
}
}
cout << solve () << endl;
}
Luogu P3886 [JLOI2009]神秘的生物 最小表示法,轮廓线DP,插头DP,动态规划的更多相关文章
- [JLOI2009]神秘的生物
题目链接 题目大意 给定一个\(n*n\)的矩阵,从其中选取恰好一个连通块,使选取的格子所对应的权值和最大. \(n\leq 9\) 解题思路 由于\(n\)特别小,考虑插头dp. 和一般的插头dp不 ...
- [JLOI2009]神秘的生物——轮廓线DP
原题链接 题目大意 \(n\times n\)的带权方阵,选一个权值最大的连通块 Solution 一眼连通性DP,然后就没了 转移很好想的啦,简单讨论一下就行了 有一个坑点,就是不能一个格子都不选, ...
- Luogu P3170 [CQOI2015]标识设计 状态压缩,轮廓线,插头DP,动态规划
看到题目显然是插头\(dp\),但是\(n\)和\(m\)的范围似乎不是很小.我们先不考虑复杂度设一下状态试试: 一共有三个连通分量,我们按照\(1,2,3\)的顺序来表示一下.轮廓线上\(0\)代表 ...
- luogu P5043 【模板】树同构 hash 最小表示法
LINK:模板 树同构 题目说的很迷 给了一棵有根树 但是重新标号 言外之意还是一棵无根树 然后要求判断是否重构. 由于时无根的 所以一个比较显然的想法暴力枚举根. 然后做树hash或者树的最小表示法 ...
- HDU 4162 Shape Number (最小表示法)
题意:给你一串n个数,求出循环来看一阶差的最小字典序:数字串看成一个顺时针的环,从某一点开始顺时针循环整个环,保证字典序最小就是答案 例如给你 2 1 3 就会得到(1-2+8 注意题意负数需要加8) ...
- POJ 1635 树的最小表示法/HASH
题目链接:http://poj.org/problem?id=1635 题意:给定两个由01组成的串,0代表远离根,1代表接近根.相当于每个串对应一个有根的树.然后让你判断2个串构成的树是否是同构的. ...
- HDU 2609 最小表示法
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2609 题意:给定n个循环链[串],问有多少个本质不同的链[串](如果一个循环链可以通过找一个起点使得和 ...
- HDU 4162 最小表示法
题目:http://acm.hdu.edu.cn/showproblem.php?pid=4162 题意:给定一个只有0-7数字组成的串.现在要由原串构造出一个新串,新串的构造方法:相邻2个位置的数字 ...
- POJ 1509 最小表示法
题目链接:http://poj.org/problem?id=1509 题意:给定一个字符串,求一个起点使字符串从该起点起的字符串字典序最小[题目的字符串起点从1开始] 思路:最小表示法模板题 #de ...
随机推荐
- Xpath 和Css Selector使用
Xpath是xml的路径语言,就是通过元素的路径来查找标签元素. Xpath直接在火狐浏览器的firebug中练习,49版本一下的火狐才能用firebug插件. Xpath的使用方法 注://* ...
- flutter 添加全局环境变量
flutter安装好了之后 要添加全局环境变量才可以在终端通过flutter命令来操作 安装flutter环境变量 vim ~/.bash_profile (不存在就创建,添加下面一行命令) expo ...
- Summernote文本编辑器入门
1.summernote是一个界面比较简洁美观的富文本编辑器. 2.文件导入(官方下载地址:http://summernote.org/) 下载回来的文件夹是这样的: 插件的核心文件放在 dist 这 ...
- java:LeakFilling(IO流)
1.IO流中缓冲区过小,会造成读入不全(打印出来的东西会不全)读入的会是最后一个读入的,不会造成写出不全(即写出来的东西).2.缓冲区在读入时的作用就是将某个文件内容的读入到缓冲区,然后通过缓冲区来进 ...
- leaflet的入门开发(一)
2016年9月27日—1.0leaflet,最快的,最稳定和严谨的leaflet,终于出来了! leaflet是领先的开源JavaScript库为移动设备设计的互动地图.重33 KB的JS,所有映射大 ...
- USACO3.3 A Game【区间dp】
这道题也是一道非常有意思的区间$dp$,和在纪中的这道题有点像:取数游戏 (除了取数规则其它好像都一样诶) 当时在纪中的时候就觉得这个$dp$非常不好想,状态定义都不是很容易想到. 但是做过一道这种题 ...
- 【Python开发】【神经网络与深度学习】网络爬虫之python实现
一.网络爬虫的定义 网络爬虫,即Web Spider,是一个很形象的名字. 把互联网比喻成一个蜘蛛网,那么Spider就是在网上爬来爬去的蜘蛛. 网络蜘蛛是通过网页的链接地址来寻找网页的. 从网站某一 ...
- python 列表的(总结)
列表(自我总结) 1.在python中什么是列表 列:排列,表:一排数据 在python中的表达就是 l = [1,2,3,4,5,6,7] 2.列表是可变类型还是不可变类型 也就是说列表能不能被ha ...
- 详解MySql的配置文件my.cnf
1.Windows下MySQL的配置文件是my.ini,一般会在安装目录的根目录. 2.Linux下MySQL的配置文件是my.cnf,一般会放在/etc/my.cnf,/etc/mysql/my.c ...
- CMD 显示当前时间和日期
1. 其实还是应该多看 help 要知道 help 比百度还用一百倍 除了 可能东西比较多 C:\Users\Administrator>date /? 显示或设置日期. DATE [/T | ...