亲手写掉的第一道最小表示法!哈哈哈太开心啦~

不同于以往的几个插头\(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,动态规划的更多相关文章

  1. [JLOI2009]神秘的生物

    题目链接 题目大意 给定一个\(n*n\)的矩阵,从其中选取恰好一个连通块,使选取的格子所对应的权值和最大. \(n\leq 9\) 解题思路 由于\(n\)特别小,考虑插头dp. 和一般的插头dp不 ...

  2. [JLOI2009]神秘的生物——轮廓线DP

    原题链接 题目大意 \(n\times n\)的带权方阵,选一个权值最大的连通块 Solution 一眼连通性DP,然后就没了 转移很好想的啦,简单讨论一下就行了 有一个坑点,就是不能一个格子都不选, ...

  3. Luogu P3170 [CQOI2015]标识设计 状态压缩,轮廓线,插头DP,动态规划

    看到题目显然是插头\(dp\),但是\(n\)和\(m\)的范围似乎不是很小.我们先不考虑复杂度设一下状态试试: 一共有三个连通分量,我们按照\(1,2,3\)的顺序来表示一下.轮廓线上\(0\)代表 ...

  4. luogu P5043 【模板】树同构 hash 最小表示法

    LINK:模板 树同构 题目说的很迷 给了一棵有根树 但是重新标号 言外之意还是一棵无根树 然后要求判断是否重构. 由于时无根的 所以一个比较显然的想法暴力枚举根. 然后做树hash或者树的最小表示法 ...

  5. HDU 4162 Shape Number (最小表示法)

    题意:给你一串n个数,求出循环来看一阶差的最小字典序:数字串看成一个顺时针的环,从某一点开始顺时针循环整个环,保证字典序最小就是答案 例如给你 2 1 3 就会得到(1-2+8 注意题意负数需要加8) ...

  6. POJ 1635 树的最小表示法/HASH

    题目链接:http://poj.org/problem?id=1635 题意:给定两个由01组成的串,0代表远离根,1代表接近根.相当于每个串对应一个有根的树.然后让你判断2个串构成的树是否是同构的. ...

  7. HDU 2609 最小表示法

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2609 题意:给定n个循环链[串],问有多少个本质不同的链[串](如果一个循环链可以通过找一个起点使得和 ...

  8. HDU 4162 最小表示法

    题目:http://acm.hdu.edu.cn/showproblem.php?pid=4162 题意:给定一个只有0-7数字组成的串.现在要由原串构造出一个新串,新串的构造方法:相邻2个位置的数字 ...

  9. POJ 1509 最小表示法

    题目链接:http://poj.org/problem?id=1509 题意:给定一个字符串,求一个起点使字符串从该起点起的字符串字典序最小[题目的字符串起点从1开始] 思路:最小表示法模板题 #de ...

随机推荐

  1. iframe嵌套的页面之间传值问题

    项目中很多时候会遇到需要用 iframe 嵌套页面的情况.有时候会有这样的需求: iframe 嵌套的页面 A ,点击之后要跳到页面 B ,但是同时还需要 A 页面中的某个属性值. 此时可以先把 A ...

  2. Wireshark 学习笔记 Lebal:Research

    学习Wireshark主要是为了契合我最近做的线性激光雷达项目,主要用于抓取数据包 首先是三本书比较值得一看,第一本是清华大学出版社的,侧重教学,第二三两本是人民邮电出版社的,其中第二本是许多课程的指 ...

  3. elasticsearch-6.2.3单机搭建

    1.这里说明下,elasticsearch为了保证安全性需要创建新的用户名,需要在新的用户名下运行,本人用的是elasticsearch-6.2.3,centos7.3 2.切换目录到:cd usr/ ...

  4. NFA转换为等价的DFA

    在编译系统中,词法分析阶段是整个编译系统的基础.对于单词的识别,有限自动机FA是一种十分有效的工具.有限自动机由其映射f是否为单值而分为确定的有限自动机DFA和非确定的有限自动机NFA.在非确定的有限 ...

  5. PHP操作redis部分命令

    //连接本地的 Redis 服务 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->auth('12345 ...

  6. BZOJ3129方程(SDOI2013)

    https://blog.csdn.net/Maxwei_wzj/article/details/80152116 对变量有上界限制及下界限制.对于下界,可以从总数中减去即可,对于上界,容斥定理.

  7. SVM之KKT条件理解

    在SVM中,我们的超平面参数最终只与间隔边界上的向量(样本)有关,故称为支持向量机. 求解最优超平面,即求最大化间隔,或最小化间隔的倒数:||w||2/2,约束条件为yi(wTxi+b)>=1 ...

  8. 【VS开发】浅谈UDP(数据包长度,收包能力,丢包及进程结构选择)

    UDP数据包长度 UDP数据包的理论长度 udp数据包的理论长度是多少,合适的udp数据包应该是多少呢?从TCP-IP详解卷一第11章的udp数据包的包头可以看出,udp的最大包长度是2^16-1的个 ...

  9. 升级tableau版本

    下载地址: https://www.tableau.com/zh-cn/support/releases 2018以后的版本升级:https://help.tableau.com/current/se ...

  10. [转帖]中国AI芯“觉醒”的五年

    中国AI芯“觉醒”的五年 https://www.cnbeta.com/articles/tech/857863.htm 原来 海思的营收已经超过了按摩店(AMD) 没想到.. 十多款芯片问世,多起并 ...