【弦图染色】bzoj 1006: [HNOI2008]神奇的国度
1006: [HNOI2008]神奇的国度
Description
K国是一个热衷三角形的国度,连人的交往也只喜欢三角原则.他们认为三角关系:即AB相互认识,BC相互认识,CA
相互认识,是简洁高效的.为了巩固三角关系,K国禁止四边关系,五边关系等等的存在.所谓N边关系,是指N个人 A1A2
…An之间仅存在N对认识关系:(A1A2)(A2A3)…(AnA1),而没有其它认识关系.比如四边关系指ABCD四个人 AB,BC,C
D,DA相互认识,而AC,BD不认识.全民比赛时,为了防止做弊,规定任意一对相互认识的人不得在一队,国王相知道,
最少可以分多少支队。
Input
第一行两个整数N,M。1<=N<=10000,1<=M<=1000000.表示有N个人,M对认识关系. 接下来M行每行输入一对朋
友
Output
输出一个整数,最少可以分多少队
Sample Input
4 5
1 2
1 4
2 4
2 3
3 4
Sample Output
3
HINT
一种方案(1,3)(2)(4)
题目大意
RT中文题干,没什么好解释的
解题思路
首先介绍两个概念
弦(chord):连接环中不相邻的两个点的边。
弦图(chordal graph):一个无向图称为弦图当图中任意长度大于3的环都至少有一个弦。
根据题目大意,我们不难发现,题目所描述的人际关系网,就正好是一张弦图。而对一张弦图进行染色,我们需要引入完美消除序列的概念:
完美消除序列(perfect elimination ordering)
定义:一个点的序列(每个点出现且恰好出现一次)v1, v2, …, vn满足vi在{vi, vi+1,…,vn}的诱导子图中为一个单纯点。
单纯点(simplicial vertex):
设N(v)表示与点v相邻的点集。一个点称为单纯点当{v} + N(v)的诱导子图为一个团。
诱导子图(induced subgraph):
图
团(clique)
图G的一个子图 G’=(V’,E’) , G’ 为关于V’ 的完全图(完全图是一个简单的无向图,其中每对不同的顶点之间都恰连有一条边相连。)。
由于完美消除序列中每个点对于其后的点构成的诱导子图,都是单纯点,因此染色只需要从后向前燃上可以染的最小的颜色,就可以保证相邻的点的颜色不同
即可获得最后的答案。
如何获得完美消除序列
最大势算法
我们设每个点的度数为该点在被标记前其连接的已经标记的点的数量。算法中,每次选取所有未标记的点中序号最大的点进行标记,每次一个点就将其加入一个序列,最后将此序列即是完美消除序列
由于度数即代表了其所在的团的点数-1,所以度数加一即是这个团需要的颜色,而最终的答案就是最大的那个团的点数
AC代码
/**************************************************************
Problem: 1006
User: FlyWhite
Language: C++
Result: Accepted
Time:1756 ms
Memory:17000 kb
****************************************************************/
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e4+5;
const int M=2e6+5;
int head[N];
int to[M],nxt[M];
int tot;
int du[N];
bool vis[N];
void AddEdge(int u,int v)
{
nxt[tot]=head[u];
to[tot]=v;
head[u]=tot++;
}
int main()
{
tot=0;
int n,m;
scanf("%d%d",&n,&m);
memset(nxt,-1,sizeof(nxt));
memset(du,0,sizeof(du));
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
AddEdge(u,v),AddEdge(v,u);
}
for(int i=n;i>=1;i--)
{
int t=0;
for(int j=1;j<=n;j++)
{
if(!vis[j]&&du[j]>=du[t]) t=j;
}
vis[t]=1;
for(int j=head[t];j!=-1;j=nxt[j])
{
if(!vis[to[j]])du[to[j]]++;
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
ans=max(ans,du[i]);
}
cout<<ans+1<<endl;
}