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

27 篇文章 0 订阅
7 篇文章 0 订阅

平面图 / 平面图判定

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

题目大意

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

思路

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

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

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

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

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

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

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

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

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

然后你就直接判断,如果 m > 3 × n − 6 m>3\times n-6 m>3×n6 就直接是 NO
那你的 m m m 就变成了 1 0 2 ∼ 1 0 3 10^2\sim10^3 102103 级别的,就可以 m 2 m^2 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值