弦图
弦图
- 弦图是一种有着特殊性质的图,部分 \(NP-Hard\) 问题可以在弦图上解决
性质/定义:
- 团:点集,满足任意两点之间都有边相连
- 团数: 最大团的点数
- 最小染色数:满足任意一条边所连两点颜色不同的情况下,颜色种类数的最小值
- 最大独立集:点集,满足任意两点之间没有边直接相连
- 最小团覆盖:用团覆盖整张图的最少团数
- 弦图:任意长度大于 \(3\) 的环都有一个弦
- 单纯点:若点 \(x\) 和与它相连的所有的点的导出子图为一个团,那么我们称 \(x\) 为单纯点
- 完美消除序列:1到 \(n\) 的排列{\(v_1,v_2,\dots v_n\)} 满足 \(v_i\) 在 {\(v_{i+1}\dots v_n\)} 的导出子图中是单纯点
结论:
-
团数 \(\le\) 色数
证明: 对最大团的导出子图进行染色,至少需要等于团数种颜色
-
最大独立集数 \(\le\) 最小团覆盖数
证明:每个团中只可能选 \(0/1\) 个
-
弦图的任意导出子图还是弦图
-
弦图的任意两点之间的最小点割集的导出子图一定是一个团
-
\(u,v\)的点割集将弦图划分成许多连通块,点割集中一定包含 \(u,v\) 所在连通块的点
(搞不懂这个结论有什么用)
-
任何一个弦图都有至少一个单纯点,任何一个不是完全图的弦图都有至少两个不相邻的单纯点
-
一个无向图是弦图当且仅当它有一个完美消除序列
相关知识点
-
弦图的判定:
-
最大势算法 (MCS):求一张弦图的完美消除序列,逆序给点编号,记 \(label_x\) 表示与 \(x\) 相邻的已标号的点的个数,每次找出 \(label\) 最大的点进行标号,并且更新 \(label\) 复杂度 \(O(n+m)\)
-
完美消除序列的判定:对于 \(MCS\) 求出的序列,我们要判断其是否是一个完美消除序列,从前向后遍历序列,从前向后找出与 \(v_i\) 在原图上直接相邻的点 \(x_1,x_2\dots x_k\) 判断 \(x_1\) 与 \(x_i\) 是否都有点相连,复杂度 \(O(n+m)\)
-
代码:
#include<bits/stdc++.h> #pragma GCC optimize(2) #define pii pair<int,int> #define mk(x,y) make_pair(x,y) #define lc rt<<1 #define rc rt<<1|1 #define pb push_back #define fir first #define sec second #define inl inline #define reg register using namespace std; namespace zzc { inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } const int maxn = 1005; const int maxm = 2e6+5; int n,m,ti,now; int g[maxn][maxn],label[maxn],seq[maxn],rk[maxn]; vector<int> v[maxn],son[maxn]; bool ins[maxn]; void msc() { int best=0; for(int i=1;i<=n;i++) v[0].pb(i),ins[i]=false; for(int i=n;i;i--) { bool flag=false; while(!flag) { for(int j=v[best].size()-1;j>=0;j--) { if(!ins[v[best][j]]) { now=v[best][j]; flag=true; break; } else v[best].pop_back(); } if(!flag) best--; } seq[i]=now;rk[now]=i;ins[now]=true; for(auto j:son[now]) { if(!ins[j]) { v[++label[j]].pb(j); best=max(best,label[j]); } } } } bool check() { for(int i=1;i<=n;i++) { now=-1; for(auto j:son[seq[i]]) { if(rk[j]<rk[seq[i]]) continue; if(now==-1||rk[now]>rk[j]) now=j; } for(auto j:son[seq[i]]) { if(rk[j]<rk[seq[i]]) continue; if(now!=j&&g[now][j]!=ti) { return false; } } } return true; } void clear() { for(int i=0;i<=n;i++) son[i].clear(),v[i].clear(),label[i]=0; } void work() { int a,b; n=read();m=read(); while(n||m) { ti++; for(int i=1;i<=m;i++) { a=read();b=read(); g[a][b]=g[b][a]=ti; son[a].pb(b);son[b].pb(a); } msc(); if(check()) puts("Perfect"); else puts("Imperfect"); clear(); n=read();m=read(); } } } int main() { zzc::work(); return 0; }
-
-
弦图的极大团:求出完美序列,找出每一个点作为单纯点时和序列后面的点形成的团,判断是否为最大团,一张图最多有 \(n\) 个极大团
-
弦图的色数/团数: 从后往前对完美消除序列上的每一个点进行染色,共用了 \(t\) 种颜色,由于团上每一个点都不同色所以 \(t=团数\ge 色数\) 由结论 1 可得 $色数\ge 团数 $ 所以 团数=色数 ,染色复杂度 \(O(n+m)\) ,特别,如果不要求染色方案数 团数 = \(max|{x}+N(x)|=max(label_x+1)\)
-
弦图最大独立集/最小团覆盖:对于完美消除序列从前向后不断选能选的点,然后每次更新一下不能选的点,复杂度 \(O(n+m)\)
例题:
SP5446 FISHNET - Fishing Ne
没什么可说的,弦图判定裸题
P3196 [HNOI2008]神奇的国度
没什么可说的 +1 , 弦图色数裸题
P3852 [TJOI2007]小朋友
没什么可说的 +2, 弦图最大独立集裸题