【ybt金牌导航3-6-5】【luogu P3209】平面图 / 平面图判定

平面图 / 平面图判定

题目链接:ybt金牌导航3-6-5 / luogu P3209

题目大意

给你一个图,保证图存在哈密顿路径(即图中存在一个包含所有顶点的环),且给出。
问你它是否是一个平面图。
如果能将一个无向图画在平面上使得每两个无重合顶点的边都不会相交,那这个图就是平面图。

思路

我们看到这个东西似乎没有思路,考虑把样例的两个图搞出来:
在这里插入图片描述
在这里插入图片描述
第二个可以这非常明显,但为什么第一个不行呢?

你会发现把哈密顿路径的边去掉,里面的边无论怎么搞都会交在一起。
那你就会想你是不是判断一下里面的会不会交在一起就可以了。

至于怎么判断,我们可以先按哈密顿路径把点重新编号。
在这里插入图片描述
那考虑到如果两条边 (a,b)(c,d) 有交点,那它们会满足这样的关系:a<c<b<d
简称把环搞成一条链之后着两条边有重合的部分。(当然 c<a<d<b 也行)

但是你以为这样就结束了?
我们把原来不行的样例中 (3,4) 这条边去掉。
在这里插入图片描述
你以为它不行,但它其实可以这样:
在这里插入图片描述
什么意思呢?就是它连不是哈密顿的边的时候不一定要连在环的里面,它是可以在外面连的!

那就相当于对于一条不在哈密顿路径上的边,它可以在里面连,也可以在外面连,那原来的相交关系只要用这两个边一个里面一个外面就不会影响。
那也就说如果两个边之间有矛盾关系,那一个选了里面另一个就一定要外面,一个选了外面另一个就一定要在里面。

然后你发现它就是 2-sat。

但是你会发现你不是哈密顿距离上的边太多了,大概是 104 级别,你两两之间判断是否相交就炸了啊。

这时候就要用到平面图的性质了。
要用到的性质是如果一个图是平面图,点的个数是 n,边的个数是 m,则一定满足 m3×n6

关于这里的证明就不多讲了,可以去看看一个 PPT。
图论4-6 平面图
(当然你上网直接搜也应该搜得到)

然后你就直接判断,如果 m>3×n6 就直接是 NO
那你的 m 就变成了 102103 级别的,就可以 m2 搞了。

代码

#include<cstdio> #include<cstring> #include<iostream> using namespace std; struct node { int to, nxt; }e[5000001]; int T, n, m, x, y, dy[10001]; int v[10001], lx[10001], ly[10001]; int le[10001], KK, low[10001], dfn[10001]; int tmp, tot, sta[10001], in[10001]; bool a[501][501], yes; void csh() { memset(a, 0, sizeof(a)); KK = 0; memset(le, 0, sizeof(le)); memset(dfn, 0, sizeof(dfn)); tmp = 0; tot = 0; memset(in, 0, sizeof(in)); } void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; } void tarjan(int now) { dfn[now] = low[now] = ++tmp; sta[++sta[0]] = now; for (int i = le[now]; i; i = e[i].nxt) if (!dfn[e[i].to]) { tarjan(e[i].to); low[now] = min(low[now], low[e[i].to]); } else if (!in[e[i].to]) low[now] = min(low[now], low[e[i].to]); if (dfn[now] == low[now]) { in[now] = ++tot; while (sta[sta[0]] != now) { in[sta[sta[0]]] = tot; sta[0]--; } sta[0]--; } } int main() { scanf("%d", &T); while (T--) { csh(); scanf("%d %d", &n, &m); for (int i = 1; i <= m; i++) { scanf("%d %d", &x, &y); a[x][y] = 1; a[y][x] = 1; } for (int i = 1; i <= n; i++) { scanf("%d", &v[i]); dy[v[i]] = i; } if (n * 3 - 6 < m) {//平面图定理 printf("NO\n"); continue; } v[n + 1] = v[1]; for (int i = 1; i <= n; i++) a[v[i]][v[i + 1]] = a[v[i + 1]][v[i]] = 0; m = 0;//找到不在哈密顿路径上的边 for (int i = 1; i <= n; i++) for (int j = 1; j < i; j++) if (a[i][j]) { m++; lx[m] = i; ly[m] = j; if (dy[lx[m]] > dy[ly[m]]) swap(lx[m], ly[m]); } for (int i = 1; i <= m; i++)//判断这些边之间那些会相交(相交就不能同时摆在里面 / 外面) for (int j = 1; j < i; j++) if (dy[lx[i]] < dy[lx[j]] && dy[lx[j]] < dy[ly[i]] && dy[ly[i]] < dy[ly[j]]) { add(i, m + j); add(j, m + i); add(m + i, j); add(m + j, i); } else if (dy[lx[j]] < dy[lx[i]] && dy[lx[i]] < dy[ly[j]] && dy[ly[j]] < dy[ly[i]]) { add(i, m + j); add(j, m + i); add(m + i, j); add(m + j, i); } for (int i = 1; i <= m + m; i++) if (!dfn[i]) tarjan(i); yes = 1; for (int i = 1; i <= m; i++) if (in[i] == in[m + i]) { yes = 0; break; } if (yes) printf("YES\n"); else printf("NO\n"); } return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_3-6-5.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(44)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示