弦图
弦图是一种特殊的图,很多在一般图上的 NP 问题在弦图上都有优秀的线性时间复杂度算法。
一些定义与性质¶
子图 :点集和边集均为原图点集和边集子集的图。
导出子图(诱导子图) :点集为原图点集子集,边集为所有满足 两个端点均在选定点集中 的图。
团 :完全子图。
极大团 :不是其他团子图的图。
最大团 :点数最大的团。
团数 :最大团的点数,记为
最小染色 :用最少的颜色给点染色使得所有边连接的两点颜色不同。
色数 :最小染色的颜色数,记为
最大独立集 :最大的点集使得点集中任意两点都没有边直接相连。该集合的大小记为
最小团覆盖 :用最少的团覆盖所有的点。使用团的数量记为
弦 :连接环中不相邻两点的边。
弦图 :任意长度大于
Lemma 1 :团数
证明:考虑单独对最大团的导出子图进行染色,至少需要
Lemma 2 :最大独立集数
证明:每个团中至多选择一个点。
Lemma 3 :弦图的任意导出子图一定是弦图。
证明:如果弦图有导出子图不是弦图,说明在这个导出子图上存在大于
Lemma 4 :弦图的任意导出子图一定不可能是一个点数大于
证明:一个点数大于
弦图的判定¶
问题描述¶
给定一个无向图,判断其是否为弦图。
点割集¶
对于图
Lemma 5 :图关于
证明:若
Lemma 6 :弦图上任意两点间的极小点割集的导出子图一定为一个团。
证明:极小点割集大小
否则,设极小点割集上有两点为
由于
若这条弦连接了
由此,可证弦图中每个极小点割集中的两点都有边直接相连,故性质得证。
单纯点¶
设
Lemma 7 :任何一个弦图都至少有一个单纯点,不是完全图的弦图至少有两个不相邻的单纯点。
证明:数学归纳法。单独考虑每一连通块。
归纳基底:当图与完全图同构时,图上任意一点都是单纯点。当图的点数
若图上的点数
由于每次将整个图分成若干个连通块证明,大小一定减小,且都满足性质,故归纳成立。
完美消除序列¶
令
Lemma 8 :一个无向图是弦图当且仅当其有一个完全消除序列。
充分性:点数为
必要性:假设有无向图存在结点数
朴素算法¶
每次找到一个 单纯点
将点
重复以上过程,若所有点都被删除,则原图是弦图且求得了一个完美消除序列;若图上不存在单纯点,则原图不是弦图。
时间复杂度
MCS 算法¶
最大势算法 (Maximum Cardinality Search)是一种可以在
逆序给结点编号,即按从
设
用链表维护对于每个
由于每条边对
正确性证明 :
设编号为
当一个点
若
考虑使用反证法。假设存在这样的
为了更方便地分析问题,我们将原图划分为几个点集。与
Lemma 9 :删去
证明:若删去后仍连通,考虑
对称同理。
Lemma 10 :删去
证明:若删去后仍连通,设选择的两点之间的最短路为
以上两个定理保证了将与
Theorem 1 :在对弦图使用最大势算法时,当一个点被标记时,它在已经标记结点中一定为一个单纯点。
证明:当我们标记了
若接下来标记的是
故此时
至此,最大势算法的正确性得证。
参考代码:
while (cur) {
p[cur] = h[nww];
rnk[p[cur]] = cur;
h[nww] = nxt[h[nww]];
lst[h[nww]] = 0;
lst[p[cur]] = nxt[p[cur]] = 0;
tf[p[cur]] = true;
for (vector<int>::iterator it = G[p[cur]].begin(); it != G[p[cur]].end();
it++)
if (!tf[*it]) {
if (h[deg[*it]] == *it) h[deg[*it]] = nxt[*it];
nxt[lst[*it]] = nxt[*it];
lst[nxt[*it]] = lst[*it];
lst[*it] = nxt[*it] = 0;
deg[*it]++;
nxt[*it] = h[deg[*it]];
lst[h[deg[*it]]] = *it;
h[deg[*it]] = *it;
}
cur--;
if (h[nww + 1]) nww++;
while (nww && !h[nww]) nww--;
}
如果此时原图是弦图,此时求出的就是完美消除序列;但是由于原图可能不是弦图,此时求出的一定不是完美消除序列,所以问题转化为 判断求出的序列是否是原图的完美消除序列 。
判断一个序列是否是完美消除序列¶
朴素算法¶
根据定义,依次判断完美消除序列
优化后的算法¶
根据完美消除序列的定义,设
jud = true;
for (int i = 1; i <= n; i++) {
cur = 0;
for (vector<int>::iterator it = G[p[i]].begin(); it != G[p[i]].end(); it++)
if (rnk[p[i]] < rnk[*it]) {
s[++cur] = *it;
if (rnk[s[cur]] < rnk[s[1]]) swap(s[1], s[cur]);
}
for (int j = 2; j <= cur; j++)
if (!st[s[1]].count(s[j])) {
jud = false;
break;
}
}
if (!jud)
printf("Imperfect\n");
else
printf("Perfect\n");
至此, 弦图判定问题 可以在
弦图的极大团¶
令
证明:考虑弦图的一个极大团
弦图最多有
设
设
问题转化为判断是否存在
for (int i = 1; i <= n; i++) {
cur = 0;
for (vector<int>::iterator it = G[p[i]].begin(); it != G[p[i]].end(); it++)
if (rnk[p[i]] < rnk[*it]) {
s[++cur] = *it;
if (rnk[s[cur]] < rnk[s[1]]) swap(s[1], s[cur]);
}
fst[p[i]] = s[1];
N[p[i]] = cur;
}
for (int i = 1; i <= n; i++) {
if (!vis[p[i]]) ans++;
if (N[p[i]] >= N[fst[p[i]]] + 1) vis[fst[p[i]]] = true;
}
弦图的色数/弦图的团数¶
一种构造方法:按完美消除序列从后往前依次给每个点染色,给每个点染上可以染的最小颜色。时间复杂度
正确性证明:设以上方法使用了
无需染色方案,只需求出弦图的色数/团数时,可以取
for (int i = 1; i <= n; i++) ans = max(ans, deg[i] + 1);
弦图的最大独立集/最小团覆盖¶
最大独立集:完美消除序列从前往后,选择所有没有与已经选择的点有直接连边的点。
最小团覆盖:设最大独立集为
正确性证明:设以上方案独立集数和团覆盖数为
for (int i = 1; i <= n; i++)
if (!vis[p[i]]) {
ans++;
for (vector<int>::iterator it = G[p[i]].begin(); it != G[p[i]].end(); it++)
vis[*it] = true;
}
习题¶
参考资料¶
buildLast update and/or translate time of this article,Check the history
editFound smelly bugs? Translation outdated? Wanna contribute with us? Edit this Page on Github
peopleContributor of this article OI-wiki
translateTranslator of this article Visit the original article!
copyrightThe article is available under CC BY-SA 4.0 & SATA ; additional terms may apply.