弦图

弦图的定义

若一个图的任意一个诱导子图都不是k阶环(k>=4),那么这个图是一个弦图

单纯点的定义

若某点v满足 { v } ∪ { u ∣ 存 在 边 ( u , v ) } \left\{v \right\}\cup\left\{u\mid存在边(u,v)\right\} {v}{u(u,v)}的诱导子图是一个团则称该点是一个单纯点

完美消除序列定义

若一个序列 { v 1 , v 2 , v 3 . . . . v n } \left\{v_{1},v_{2},v_{3}....v_{n}\right\} {v1,v2,v3....vn}满足任意i使得 { v i , v i + 1 . . v n } \left\{v_{i},v_{i+1}..v_{n}\right\} {vi,vi+1..vn}的诱导子图中 v i v_{i} vi是一个单纯点,则称该序列是一个完美消除序列

弦图的性质

1.弦图中至少存在一个单纯点,若该图不是一个完全图,那么必然存在至少两个不相邻的单纯点

证明:若一个图是一个完全图,那么显然,否则我们考虑任意一个非完全图,那么他必然存在两个及以上的极大团,而且这两个极大团必然满足对应两个不相邻的单纯点(感性)

2.一个弦图的诱导子图必定也是弦图
3.若一个图是一个弦图,当且仅当存在一个完美序列

证明:
1.若一个图是弦图,由性质1可知他至少存在一个单纯点v,那么我们可以构造完美消除序列,删除点v及其相连的边那么剩下的图必然也是一个弦图,数学归纳即可
2.若一个图存在完美序列,则他必定是弦图。若不是我们考虑长度大于3的某一个环,不妨假设其中最先在完美消除序列中出现的点为v,则必然存在与其相邻的点v1,v2那么根据完美消除序列的定义,v1与v2之间必然有边,所以上述成立

4.最大团的点数=最小染色数
5.最大独立集=最小团覆盖

如何求完美消除序列

最大势算法(MCS)

我们逆回来构造完美消除序列,每次选取当前剩余图中的与被选取点相连最多的点,若该图是一个弦图,则可以证明这样构造出来的必然是完美消除序列,同时我们还可以利用这个算法判断一个图是不是弦图

弦图的判断

我们通过判断原图中是否存在完美消除序列
我们通过MCS构造出一个(伪)完美消除序列,然后对于每一个点v,考虑与其相连并在序列中在其之后出现的点集 { v 1 , v 2 , v 3.... v k } \left\{v1,v2,v3....vk\right\} {v1,v2,v3....vk}(这里的点按在序列中出现的先后排列),那么我们只需要判断v1与v2,v3…vk是否有边即可,证明显然

[HNOI2008]神奇的国度

求弦图的最小染色数,由于最大团一定是极大团,而且一个极大团一定是一个单纯点与其相连的点的诱导子图
直接MCS

#include<bits/stdc++.h>
#define pb(x) push_back(x)
using namespace std;
const int N=1e4+10;
vector<int>e[N],q[N];
int label[N];
bool vis[N];
int n,m,x,y,s;
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		e[x].pb(y); e[y].pb(x);
	}
	for (int i=1;i<=n;i++) q[0].pb(i);
	int ans=0;
	for (int i=n;i>=1;i--){
		int now=0;
		while (1) {
			bool p=0;
			for (int i=q[ans].size()-1;~i;i--){
				if (vis[q[ans][i]]) q[ans].pop_back();
				else {
					now=q[ans][i]; p=1; break;
				}
			}
			if (!p) ans--; else break;
		}
		vis[now]=1;
		for (int i=0;i<e[now].size();i++){
			int u=e[now][i];
			if (vis[u]) continue;
			label[u]++; q[label[u]].pb(u); ans=max(ans,label[u]);
		}
		s=max(s,ans);
	}
	printf("%d\n",s+1);
}

[TJOI2007]小朋友

求弦图的最大独立集
对于完美消除序列从前往后贪心即可

#include<bits/stdc++.h>
#define pb(x) push_back(x)
using namespace std;
const int N=1e4+10;
vector<int>e[N],q[N];
bool b[N];
int label[N],a[N];
bool vis[N];
int n,m,x,y,s;
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		e[x].pb(y); e[y].pb(x);
	}
	for (int i=1;i<=n;i++) q[0].pb(i);
	int ans=0;s=0;
	for (int i=n;i>=1;i--){
		int now=0;
		while (1) {
			bool p=0;
			for (int i=q[ans].size()-1;~i;i--){
				if (vis[q[ans][i]]) q[ans].pop_back();
				else {
					now=q[ans][i]; p=1; break;
				}
			}
			if (!p) ans--; else break;
		}
		vis[now]=1;
		a[i]=now;
		for (int i=0;i<e[now].size();i++){
			int u=e[now][i];
			if (vis[u]) continue; 
			label[u]++; q[label[u]].pb(u); ans=max(ans,label[u]);
		}
	}
	for (int i=1;i<=n;i++) {
		if (!b[a[i]]) {
			s++; 
			for (int j=0;j<e[a[i]].size();j++) b[e[a[i]][j]]=1;
		}
	}
	printf("%d\n",s);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值