题目
t(t<=100)组样例,
每次给定n(n<=200)个点m(m<=10000)条边的无向图,
并给出n个点的哈密顿回路(即依次给出回路中的点号)
问该图是否为平面图
思路来源
洛谷官方题解
题解
平面图定义:图的边两两不相交
已经给出一个哈密顿回路,可以认为是一个环
对于其余的边来说,每条边有两种连的方式,要么在环内连,要么在环外连
如果两条边的均在环内/均在环外,则两条边相交当且仅当环上两点构成的区间段相交
换言之,为了使边均不相交,需要让区间段相交的边互斥,
即:若AB区间段相交,则要么A在环内连且B在环外连,要么A在环外连且B在环内连
把环内连看成0,环外连看成1,每个点有两种选择,这就是一个典型的2-sat
不过本题只需要关注0和1的连通性,所以可以转为种类并查集,判连通即可
平面图性质:若m>3n-6,一定不是平面图
可以先判掉m很大的情况,剩下的m不超过600,就可以两两判断是否相交了
注意,本题中的相交,需要是严格相交,
即不包括区间内含/区间包含但相交一个端点的情况
手玩一下就会发现,只有区间严格相交,边才会严格相交
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=205,M=1e4+10;
int t,n,m,w,rk[N],par[2*M];
struct edge{
int u,v;
void read(){
scanf("%d%d",&u,&v);
}
}e[M];
int find(int x){
return par[x]==x?x:par[x]=find(par[x]);
}
void un(int x,int y){
x=find(x),y=find(y);
par[y]=x;
}
void sol(){
scanf("%d%d",&n,&m);
for(int i=0;i<m;++i){
e[i].read();
}
for(int i=0;i<n;++i){
scanf("%d",&w);
rk[w]=i;
}
if(m>3*n-6){
puts("NO");
return;
}
for(int i=0;i<2*m;++i){
par[i]=i;
}
for(int i=0;i<m;++i){
e[i].u=rk[e[i].u];
e[i].v=rk[e[i].v];
if(e[i].u>e[i].v)swap(e[i].u,e[i].v);
}
// 相交且不包含
for(int i=0;i<m;++i){
for(int j=i+1;j<m;++j){
int x=min(e[i].v,e[j].v)-max(e[i].u,e[j].u);
if(x<=0)continue;
if(e[i].v-e[i].u>x && e[j].v-e[j].u>x){
un(i,j+m);
un(i+m,j);
}
}
}
for(int i=0;i<m;++i){
if(find(i)==find(i+m)){
puts("NO");
return;
}
}
puts("YES");
}
int main(){
scanf("%d",&t);
while(t--){
sol();
}
return 0;
}