专栏文章

弦图:从入门到入入门

算法·理论参与者 36已保存评论 38

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
38 条
当前快照
1 份
快照标识符
@mhz5tw47
此快照首次捕获于
2025/11/15 01:56
4 个月前
此快照最后确认于
2025/11/29 05:25
3 个月前
查看原文
总结了一下网上现有的零散弦图资料,并补充了部分证明。
由于笔者实力有限,若文中有任何问题,望指出!

目录

  • 前置定义
  • 引理
  • 最大势算法(MCS\text{MCS} 算法)
  • 应用
    • 弦图判定
    • 弦图染色问题/最大团问题
    • 弦图最小团覆盖/最大独立集
    • 区间图

前置定义

基础定义
  • 完全图:对于任意的点 u,vVu,v∈V,有 {uv}E\{u\rightarrow v\}∈E
  • :连接环上不相邻两个点的边。
  • 子图:点集为原图点集子集,边集为原图边集子集。
  • 导出子图:点集为原图点集子集,边集为所有满足 两个端点均在选定点集中 的图。
  • :完全子图(显然一定是导出子图)。
  • 点割集:若点集 VVu,vu,v 的点割集,则在原图中删除 VV 的导出子图后,u,vu,v 不连通。
  • 极小点割集:若点集 VVu,vu,v 的极小点割集,不存在 u,vu,v 的点割集 VV' 满足 VVV'\subsetneqq V
    V={1,5}V=\{1,5\}2,42,4 的点割集、极小点割集。
  • 最大团:点数最多的团。
  • 极大团:若点集 VV 的导出子图是极大团,则不存在点集 VV' 的导出子图是团,且 VVV\subsetneqq V'
    红色部分是原图的子图、导出子图、团、最大团、极大团。
  • 弦图:任意大于 33 的环都有弦的无向图。
    左图是弦图而右图不是。因为右图存在一个 {12341}\{1 \rightarrow 2 \rightarrow 3 \rightarrow 4 \rightarrow 1\} 的长度为 44 的环,且没有弦。

为解决弦图问题引入的定义
  • 单纯点:设与点 xx 相连的点集为 N(x)N(x),若 xx 为单纯点,则点集 V={x}+N(x)V=\{x\}+N(x) 的导出子图是一个团。
  • 完美消除序列:令 n=Vn=|V|,完美消除序列为一个 nn 的排列 {v1,v2,,vn}\{v_1,v_2,\dots,v_n\},满足 viv_i 在点集 V={vi,vi+1,,vn}V=\{v_i,v_{i+1},\dots,v_n\} 的导出子图中是单纯点。
    上述弦图存在一个完美消除序列 {4,2,1,3,5}\{4,2,1,3,5\},单纯点有 1,4,51,4,5

引理

Lemma1:\textbf{Lemma1:} 弦图的导出子图为弦图。
  • 证明:
    由定义可知,所有满足 两个端点均在选定点集中 的边都在导出子图中。若导出子图不是弦图,则在变为原图的加边过程中,肯定不能添加到一条弦切开导出子图中 4\ge 4 的环。与题设矛盾,所以该导出子图一定为弦图。

Lemma2:\textbf{Lemma2:} 在弦图中,若 u,vu,v 存在割集,则 u,vu,v极小点割集的导出子图为团。
  • 证明:
    u,vu,v 的极小点割集为 II,删去 iiu,vu,v 所在的连通块为 A,BA,B
    若极小点割集点数 =1=1,显然是团。
    若点数 2\ge 2,设极小点割集上有两点 x,yx,y,则 x,yx,y 一定与 A,BA,B 之间的点有边相连(否则 II 删去 x,yx,y 后仍是点割集,不满足极小点割集定义)。设其相连的点分别为 xa,xb,ya,ybx_a,x_b,y_a,y_b,则一定存在环 {xxayayybxbx}\{x \rightarrow x_a \sim y_a \rightarrow y \rightarrow y_b \sim x_b \rightarrow x\},其中,uvu \sim v 表示 uuvv 的最短路(默认边权为 11)。显然该环的大小一定 4\ge 4,所以环上肯定存在弦。
    因为这条弦不能连接 A,BA,B(否则 II 不是点割集),不能在 AABB 内连接(否则 xa/bya/bx_{a/b} \sim y_{a/b} 不是最短路),所以该弦一定连接 x,yx,y
    因此 II 中任意两点 x,yx,y 都有边相连,证毕。

Lemma3:\textbf{Lemma3:} 非完全图的弦图至少存在两个不相邻的单纯点。
  • 证明:
    考虑用归纳法证明。
    假设现在已经证明了点数 <V<|V| 的弦图满足引理。
    对于点集为 VV,边集为 EE 的弦图,若为完全图,显然满足引理;若不是完全图,则一定存在 (u,v)(u,v) 满足 {uv}E\{u\rightarrow v\}\notin E,也就是 u,vu,v 之间一定存在点割集。设 u,vu,v 的极小点割集为 II,设删去 II 后,uu 所在的连通块为 AA
    由于 AA 是导出子图,根据 Lemma1\textbf{Lemma1} 可知,AA 是弦图。由 Lemma2\textbf{Lemma2} 可知,II 是团,即弦图。所以图 G=I+BG=I+B 一定也是弦图,且点数 <V<|V|。所以 GG 满足引理。若 GG 是完全图,则每个点都是单纯点,AA 中一定含有单纯点;若 GG 不是完全图,由 Lemma2\textbf{Lemma2} 可知 II 是团,只有一个不相邻的单纯点,所以 AA 中一定含有单纯点。
    同理可得 BB 中也含有单纯点。又因为 A,BA,B 存在割集,所以 A,BA,B 中的单纯点不相邻,符合引理。
    又因为当点数 3\ge 3 时符合引理(手膜一下就好),所以任何弦图都符合引理,证毕。

Lemma4:\textbf{Lemma4:} 一个无向图是弦图当且仅当其有完美消除序列。
  • 证明:
    若无向图是非弦图且存在完美消除序列,找到完美消除序列中最靠前出现的点 viv_i,满足 viv_i 存在于一个 4\ge 4 且无弦的环上。设在环上与其相邻的点为 vj,vkv_j,v_k,则 vj,vkv_j,v_k 在弦图中的位置位于 viv_i 后。根据完美消除序列的定义可得 vj,vkv_j,v_k 之间有边相连,与题设矛盾。所以非弦图没有完美消除序列(必要性)。
    假设点数为 (V1)(|V|-1) 的弦图有完美消除序列,对于点数为 V|V| 的弦图,删去一个单纯点(由 Lemma2\textbf{Lemma2} 可知,弦图存在消除点),由 Lemma1\textbf{Lemma1} 可知,剩下的导出子图为弦图,存在完美消除序列;再将删除的单纯点放到完美消除序列的开头即可(充分性)。

最大势算法(MCS\text{MCS} 算法)

一种线性求弦图完美消除序列的算法。
labelxlabel_x 表示点 xx 与多少已标号的点相邻,算法流程如下:
  1. 将一个 labellabel 值最大的点标号,并插入完美消除序列的开头
  2. 更新点的 labellabel 值。
  3. 重复 1,21,2 步至所有点都被标号。

  • 正确性证明:
    γ(x)\gamma(x) 表示 xx 在最大势生成的序列中的位置。
    证明一个序列是完美消除序列,等价于证明对于序列中的数 uu,若 uuv,wv,w 相连且 γ(u)<γ(v)<γ(w)\gamma(u)<\gamma(v)<\gamma(w)v,wv,w 一定有边相连(考虑前 ii 项满足完美消除序列特征,归纳法证明即可)。
    图1 图2
    假设 vvww 没有边相连(如左图),为了保证 γ(v)>γ(u)\gamma(v)>\gamma(u),一定存在点 xx 满足 γ(x)>γ(w)\gamma(x)>\gamma(w),且 xxvv 有边相连,与 uu 没有边相连。同样还有 xxww 没有边相连,否则会出现 {uvxwu}\{u\rightarrow v \rightarrow x \rightarrow w \rightarrow u\} 的环且环上无弦,与弦图定义矛盾(如右图)。
    图3 图4
    γ(u)<γ(v)<γ(x)<γ(w)\gamma(u)<\gamma(v)<\gamma(x)<\gamma(w)(如左图),因为 x,wx,w 无边相连,u,wu,w 有边相连,为了保证 γ(u)<γ(x)\gamma(u)<\gamma(x),一定存在点 yy 满足 γ(y)>γ(x)\gamma(y)>\gamma(x),且 yyxx 相连,与 ww 不相连(否则会存在 4\ge 4 的环,如右图)。
    图5 图六
    γ(u)<γ(v)<γ(w)<γ(x)\gamma(u)<\gamma(v)<\gamma(w)<\gamma(x)(如左图),因为 x,vx,v 有边相连,x,wx,w 无边相连,为了保证 γ(v)<γ(w)\gamma(v)<\gamma(w),一定存在点 yy 满足 γ(y)<γ(w)\gamma(y)<\gamma(w)yyww 相连,与 vv 不相连(否则会存在 4\ge 4 的环,如右图)。
    可以发现,该结论会引入新节点 yy,为了使 yy 满足完美序列特征,用上述方式继续分析,会不断引入新节点。最终肯定会出现矛盾。

实现的话还有些细节。考虑开 mm 个双向链表 vvvxv_x 存储 label=xlabel=x 的所有点。
步骤 22 更新时,直接将更新后的点插入对应的 vxv_x 中并更新 max{label}\max\{label\}不用删除更新前的点。
步骤 11 找点时,暴力从当前更新max{label}\max\{label\} 开始找,不符合条件的从链表中删除(即步骤 22 中未删除的更新前的点),找到第一个符合条件的点为止。注意,若 vmax{label}v_{\max\{label\}} 中所有点都被删除了,max{label}\max\{label\} 值减一。
CPP

#include<bits/stdc++.h>
using namespace std;
const int maxn=10010;
const int maxm=1000010;
inline int read(){
	int x=0;
	char c=getchar();
	for(;!(c>='0'&&c<='9');c=getchar());
	for(;c>='0'&&c<='9';c=getchar())
		x=(x<<1)+(x<<3)+c-'0';
	return x;
}
struct edge{
	int v,to;
}e[maxm<<1];
int head[maxn],ecnt;
void addedge(int u,int v){
	e[++ecnt].v=v,e[ecnt].to=head[u],head[u]=ecnt;
}
vector<int>v[maxm];
//用vector模拟链表
int rnk[maxn],id[maxn]; 
//rnk[x]:x在完美消除序列中的位置
//id[i]:完美消除序列第i位的值
int label[maxn];
int n,m,ans;
void MCS(){
	bool flag=0;
	int Maxl=0,x;
	for(int i=1;i<=n;i++) 
		v[0].push_back(i);
	for(int t=n;t;t--){
		while(!flag){
			while(!v[Maxl].empty())
				if(rnk[*(v[Maxl].end()-1)]||label[*(v[Maxl].end()-1)]!=Maxl)
					v[Maxl].pop_back();
				//判断两种不合法的情况
				//其实第二个判断没必要,想一想为什么
				else if(label[*(v[Maxl].end()-1)]==Maxl){
					x=(*(v[Maxl].end()-1)),flag=1;
					v[Maxl].pop_back();
					break;//选点
				}	
			if(!flag) Maxl--;
			//v_Maxl中不存在合法点
		}
		id[t]=x,rnk[x]=t,flag=0;
		for(int i=head[x];i;i=e[i].to)
			if(!rnk[e[i].v]){
				v[++label[e[i].v]].push_back(e[i].v);
				Maxl=max(Maxl,label[e[i].v]);
				//插入点,更新Maxl
			}
	}
}
int main(){
	int x,y;
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		x=read(),y=read();
		addedge(x,y),addedge(y,x);
	} 
	MCS();
	return 0;
} 
  • 时间复杂度证明:
    每条边最多会让一个点加入链表中,所以链表的插入和删除复杂度为 O(m)O(m)。 每次找点时,最劣情况为 max{label}\max\{label\} 一直降到 00,所以最劣复杂度为 O(max{label})O(\sum\max\{label\})。由于每一个点只会对 max{label}\max\{label\} 进行一次有效更新(在被标号时),所以 O(max{label})=O(label)O(\sum\max\{label\})=O(\sum label)。又因为每条边只会对 label\sum label 贡献 11,所以 O(max{label})=O(label)=O(m)O(\sum\max\{label\})=O(\sum label)=O(m)
    因此总时间复杂度不高于 O(n+m)O(n+m)

应用

弦图的精髓就是最大势算法。关于弦图的所有应用,基本上都是通过最大势算法求解。
  • 弦图判定

Lemma4\textbf{Lemma4} 可知,非弦图是没有完美消除序列的,而通过最大势算法,我们一定可以得到一个 11nn 的排列,所以只需要判断该排列是否为完美消除序列即可。
暴力判定是 O(nm)O(nm) 的,考虑更高效的判定方法。
假设对于序列的后 (ni)(n-i){vi+1,,vn}\{v_{i+1},\dots,v_n\} 仍满足完美消除序列的特征,考虑判定 {vi,vi+1,,vn}\{v_i,v_{i+1},\dots,v_n\} 是否也满足完美消除序列特征。
viv_i{vi+1,,vn}\{v_{i+1},\dots,v_n\}直接相连的点为 {vc1,vc2,,vck}(ci<ci+1)\{v_{c_1},v_{c_2},\dots,v_{c_k}\}(c_i<c_{i+1}),根据完美消除序列的定义可知,V={vi,vc1,vc2,,vck}(ci<ci+1)V=\{v_i,v_{c_1},v_{c_2},\dots,v_{c_k}\}(c_i<c_{i+1}) 的导出子图是完全图,又因为 viv_i 已经与剩余的点直接相连,所以只需要满足 V={vc1,vc2,,vck}V=\{v_{c_1},v_{c_2},\dots,v_{c_k}\} 的导出子图是完全图即可。
又因为 vc1v_{c_1} 满足完美消除序列特征,所以只要 vc1v_{c_1} 与剩余的点都相连,V={vc1,vc2,,vck}V=\{v_{c_1},v_{c_2},\dots,v_{c_k}\} 的导出子图一定是完全图。
因此,对于每个点 viv_i,只需要分别求出其对应的 {vc1,vc2,,vck}\{v_{c_1},v_{c_2},\dots,v_{c_k}\},然后判断 vc1v_{c_1} 是否与剩余点直接相连即可。

将每条边 (u,v)(u,v) 压成一个数存入哈希表,每次查询即可。时间复杂度 O(m+n)O(m+n)
部分代码如下:
CPP
struct Hash{
	int u,v,to;
}H[maxm<<1];
int Head[p],hcnt;
void addhsh(int x,int u,int v){
	H[++hcnt].u=u,H[hcnt].v=v,H[hcnt].to=Head[x],Head[x]=hcnt;
}
void Insert(int u,int v){
	int x=(u*(n+1ll)+v)%p;
	for(int i=Head[x];i;i=H[i].to)
		if(H[i].u==u&&H[i].v==v) return ;
	addhsh(x,u,v);
}
bool Be(int u,int v){
	int x=(u*(n+1ll)+v)%p;
	for(int i=Head[x];i;i=H[i].to)
		if(H[i].u==u&&H[i].v==v) return 1;
	return 0;
}
//以上是哈希表 
int main(){
	int x,y;
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		x=read(),y=read();
		Insert(x,y),Insert(y,x);
		addedge(x,y),addedge(y,x);
	}
	mcs();int cnt=0,flag=1;  
	for(int i=n;i&&flag;cnt=0,i--){ 
		for(int j=head[id[i]];j;j=e[j].to)
		//找到与id[i]相连且在序列后方的所有节点,存入tmp 
			if(rnk[e[j].v]>i){
				tmp[++cnt]=e[j].v;
				if(rnk[e[j].v]<rnk[tmp[1]])
					swap(tmp[1],tmp[cnt]);
			}
		for(int j=2;j<=cnt;j++)
		//哈希判断v_c1是否与v_cj相连 
			if(!Be(tmp[1],tmp[j])){
				printf("Imperfect\n");
				flag=0;break;
			}
	}
	if(flag) printf("Perfect\n\n");
	return 0;
}	
	
  • 弦图染色问题/最大团问题

设图 GG 的最小染色数为 χ(G)\chi(G),最大团为 ω(G)\omega(G),有结论 χ(G)=ω(G)\chi(G)=\omega(G)
  • 证明:
    由于完全图每个点的颜色都不一样,所以有 χ(G)ω(G)\chi(G) \ge \omega(G)
    考虑一种构造染色数的方法,即按照完美消除序列倒序染色。设构造出的染色数为 CC
    首先一定有 Cχ(G)C \ge \chi(G)
    当对点 viv_i 进行染色时,只有 {vc1,vc2,,vck}\{v_{c_1},v_{c_2},\dots,v_{c_k}\} 中的点才会对其颜色进行限制(cc 序列沿用 弦图判定 一节的定义)。由 弦图判定 一节可知,{vi,vc1,vc2,,vck}\{v_i,v_{c_1},v_{c_2},\dots,v_{c_k}\} 是完全图,即两两颜色都不同,所以对于点 viv_i 来说,颜色数为 (k+1)(k+1)。因此,C=max{k+1}=ω(G)C=\max\{k+1\}=\omega(G)
    从而有 C=ω(G)χ(G),C=ω(G)χ(G)C=\omega(G) \ge \chi(G),C=\omega(G)\le \chi(G),得 C=ω(G)=χ(G)C=\omega(G)=\chi(G)
同时也得到了 χ(G),ω(G)\chi(G),\omega(G) 的线性求解方法。
注意,如果只求染色数而不用求染色方案,则答案为 max{labelx+1}\max\{label_x+1\}labelxlabel_x 等价于在完美消除序列中,处于点 xx 后且与点 xx 直接相连的点的个数)。
主要代码如下:
CPP
int main(){
	int x,y;
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		x=read(),y=read();
		addedge(x,y),addedge(y,x);
	} 
	MCS();
	for(int i=1;i<=n;i++)
		ans=max(ans,label[i]+1);
	//求解答案,正确性已证明
	printf("%d\n",ans);
	return 0;
} 
  • 弦图最小团覆盖/最大独立集

设图 GG 的最小团覆盖数为 κ(G)\kappa(G),最大独立集为 α(G)\alpha(G)
仍然有结论 κ(G)=α(G)\kappa(G)=\alpha(G)
  • 证明:
    由于每个团中最多选一个点,所以有 α(G)κ(G)\alpha(G) \le \kappa(G)
    考虑一种构造独立集的方法,即按照完美消除序列顺序求独立集。设选出的点数为 TT
    首先肯定有 Tα(G)T \le \alpha(G)。每个点 xx 选完后,在 xx 后方且与 xx 有边相连的点都不能选,根据完美消除序列的定义,xx 支配了一个团。所以 TT 也是一个团覆盖数,有 Tκ(G)T \ge \kappa(G)
    κ(G)Tα(G),κ(G)α(G)\kappa(G)\le T \le \alpha(G),\kappa(G) \ge \alpha(G),结合两者得 T=κ(G)=α(G)T=\kappa(G)=\alpha(G)
同时也得到了 α(G),κ(G)\alpha(G),\kappa(G) 的线性求解方法(每条边只遍历一次,显然线性)。
主要代码如下:
CPP
int main(){
	int x,y;
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		x=read(),y=read();
		addedge(x,y),addedge(y,x);
	}
	MCS();int ans=0;
	for(int i=1;i<=n;i++)
		if(!vis[id[i]]){
			ans++;
			for(int j=head[id[i]];j;j=e[j].to)
				vis[e[j].v]=1;
		}
	printf("%d\n",ans);
	return 0;
} 
另外,显然有最小点覆盖等于最小团覆盖。
  • 区间图

区间图定义:有若干个区间,把每个区间当做一个点,两个点之间有连边当且仅当两个区间有交集。
以下是一个区间图的构造:
之所以提到区间图,是因为区间图有一个很好的性质——所有的区间图都是弦图。
  • 证明:
    首先,如果环长 3\le 3,该环一定是团,可构造(可以看做环上每一条边都是弦)。
    假设区间图存在无弦的环 {v1,v2,,vk}\{v_1,v_2,\dots,v_k\},由于无弦,vi,vi+1v_i,v_{i+1} 两两交集位置一定严格单调递增或递减(可以手膜一下,如果不单调一定有弦),则 v1,vkv_1,v_k 的交集一定为空,矛盾。

对于区间问题,可以先构造区间图,再根据弦图的性质求解。
(不过有一说一这个用处不大,毕竟区间图还有很多更优的性质qwq)

评论

38 条评论,欢迎与作者交流。

正在加载评论...