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 ...
随机推荐
- ControlTemplate in WPF —— Button
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" x ...
- Python基本语法_函数_返回值
目录 目录 前言 函数的返回值 在函数中Return和Print的区别 函数中的文档 前言 函数的返回值是函数重要的组成部分.函数的根本在于实现程序的部分功能,所以很多时候我们需要将函数执行后的结果返 ...
- 四十七:数据库之alembic数据库迁移工具的基本使用
在一般情况下,如果修改了模型,如增加或者删除了字段,SQLAlchemy是不会更新的,这就需要使用alembic来实现 使用alembic步骤:一:定义好模型二:使用alembic创建一个仓库:ale ...
- 437路径总和III
题目: 给定一个二叉树,它的每个结点都存放着一个整数值.找出路径和等于给定数值的路径总数.路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点).来源: ht ...
- python3.6+RF环境搭建
现在大家都在用python3了,利用这个机会正好把自己的练习重新整理一遍,本篇记录用python3.6重新搭建关键字驱动环境 目录 1.安装python3.6 2.安装wxPython 3.安装rob ...
- Jmeter之乱码 (一)
Jmeter历史版本下载: http://archive.apache.org/dist/jmeter/binaries/ Jmeter3.0接口测试脚本POST请求主体中的中文无法正确显示,现象如下 ...
- cmd命令简单别木马的蛛丝马迹
一些基本的Windows命令往往可以识别木马的蛛丝马迹,而且在保护网络安全上起到很大的作用. 检测网络连接 如果你怀疑自己的计算机上被别人安装了木马,或者是中了病毒,但是手里没有完善的工具来检测是不是 ...
- spring boot-2.Hello world
由于 个人习惯,我选择使用STS来作为开发工具.跳过手动构建spring boot 项目的环节,直接使用向导创建spring boot 项目. 1.创建spring boot项目 File ----& ...
- 【转】mysql的group_concat函数,默认最大长度是1024
mysql的group_concat函数,默认最大长度是1024 查询sql: show variables like 'group_concat_max_len'; 设置方式: 修改配置文件my.i ...
- 好问题:count(1)、count(*)、count(列)有什么区别?
执行效果: 1. count(1) and count(*) 当表的数据量大些时,对表作分析之后,使用count(1)还要比使用count(*)用时多了! 从执行计划来看,count(1)和coun ...