题解 P3209 【[HNOI2010]平面图判定】
前言
这题其实用不上
大家的思路其实本质都差不多,代码实现起来除了
以防后面的人也会跟我一样被吓着,我决定用比较易懂的方式解释一下 qwq
一些必须想明白的事
题目中给了图中的哈密顿路径,这意味着图中的所有结点都连成了一个环。但是环上只有 n 条边,剩下的边去哪了呢?自然是把环上不相邻的两点给连接起来了
暂且先把除了哈密顿路径的所有边成为“弦”
想想环把平面分成了两个部分,那么如果两条弦相交了,使此图不为平面图,那么这两条弦肯定在环的同侧(内或外)
只在同侧就行了吗?
设结点
设有两条边
并且
把
并且
两者满足其一,同侧两弦就相交
代码写上来就是
bool Intersect(DE x,DE y)
{
return
(
(
(HamiN[y.a]<HamiN[x.a] && HamiN[x.a]<HamiN[y.b])
&&
(HamiN[x.b]<HamiN[y.a] || HamiN[y.b]<HamiN[x.b])
)
||
(
(HamiN[y.a]<HamiN[x.b] && HamiN[x.b]<HamiN[y.b])
&&
(HamiN[x.a]<HamiN[y.a] || HamiN[y.b]<HamiN[x.a])
)
);
}
注意提前预处理出
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++) E[i].Scan();
for(int i=1,V;i<=n;i++)
{
scanf("%d",&V);
HamiN[V]=i;
}
for(int i=1;i<=m;i++)
if(HamiN[E[i].a]>HamiN[E[i].b]) swap(E[i].a,E[i].b);
算法思路
如果图上所有弦不管向内向外如何摆放都会有相交,那么这个图就不是平面图
如果存在有一种摆放方式使所有弦都不相交,那么就是平面图
我们只要尽量让图成为平面图就行了
对于每一条弦,找出所有有可能与之相交的弦(就是如果放在同侧它俩就会相交,它们组成的集合称为
我们每放一条弦,就将其的
对于一开始放置的弦,我们放在哪侧都行
整个放置的过程用
效率
代码实现
#include<bits/stdc++.h>
using namespace std;
const int MAXN=200;
const int MAXM=1e4;
bool inseg(int bef,int ob,int aft)
{return bef<ob && ob<aft;}
struct DE
{
int a,b;
void Scan() {scanf("%d %d",&a,&b);}
void Print() {printf("%d %d\n",a,b);}
}E[MAXM+5];
int T,n,m;bool ans;
int HamiN[MAXN+5];
DE Assort[3][MAXN+5];int num[3];
//记录两侧有哪些弦放置
bool Intersect(DE x,DE y)
{
return
(
(
(HamiN[y.a]<HamiN[x.a] && HamiN[x.a]<HamiN[y.b])
&&
(HamiN[x.b]<HamiN[y.a] || HamiN[y.b]<HamiN[x.b])
)
||
(
(HamiN[y.a]<HamiN[x.b] && HamiN[x.b]<HamiN[y.b])
&&
(HamiN[x.a]<HamiN[y.a] || HamiN[y.b]<HamiN[x.a])
)
);
}
bool cmp(DE x,DE y)
{
if(HamiN[x.a]!=HamiN[y.a]) return HamiN[x.a]<HamiN[y.a];
return HamiN[x.b]<HamiN[y.b];
}
bool CanPutIn(DE ob,bool type)//判断弦 ob 是否能放在 type 侧
{
for(int i=1;i<=num[type];i++)
if(Intersect(ob,Assort[type][i])) return 0;
return 1;
}
struct msg
{
bool type;
int edge;
};
vector<int> oppo[MAXM+5];
bool visit[MAXM+5];
bool DFS(int edge,bool type)
{
visit[edge]=1;
Assort[type][++num[type]]=E[edge];
bool cnt=1;
for(int i=0,rear;i<oppo[edge].size() && cnt;i++)
{
rear=oppo[edge][i];
if(visit[rear]) continue;
if(CanPutIn(E[rear],!type)) cnt&=DFS(rear,!type);
else cnt=0;
}
return cnt;
}
int main()
{
for(scanf("%d",&T);T--;)
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++) E[i].Scan();
for(int i=1,V;i<=n;i++)
{
scanf("%d",&V);
HamiN[V]=i;
}
for(int i=1;i<=m;i++)
if(HamiN[E[i].a]>HamiN[E[i].b]) swap(E[i].a,E[i].b);
if(m>3*n-6) {printf("NO\n");continue;}
num[0]=num[1]=0;
ans=1;
for(int i=1;i<=m;i++)
{
oppo[i].clear();
visit[i]=0;
}
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
{
if(i==j) continue;
if(Intersect(E[i],E[j])) oppo[i].push_back(j);
}
for(int i=1;i<=m && ans;i++)
if(!visit[i]) ans&=DFS(i,0);
//如果 !visit[i],说明弦 i 跟之前放置的弦绝对不会相交,因此可以随意放置弦 i
if(ans) printf("YES\n");
else printf("NO\n");
}
return 0;
}
后语
不看也行,没啥用
一开始看到这题第一反应是库拉托夫斯基定理 qwq 满图找
然后效率是……
然后想了个假算法:依次放置所有边,能放进内侧就放内侧,不行就外侧,再不行直接输出 NO
其实写到一半一经发现算法假了,然而不甘心,按照弦两侧结点的
居然拿了 80 分 qwq 考场上如果想不出来这方法肯定值了