本题所给出的图显然满足弦图的定义,其主要性质与有关算法可以参考cdq的论文《弦图与区间图》。
这里以人为节点,人之间的认识关系就是边,而分组不能将互相认识的人分在一起,将一个组看成一种颜色的话,这就是一道最小染色的题目,但是朴素的染色方法会超时,我们可以使用弦图的性质来完成染色。而求弦图的最小染色,我们可以先求出弦图的完美消除序列,再将序列中的点顺序倒着用来贪心染色,所用到的颜色数就是答案。其实到这里就可以看着论文来写了,我就不细讲求完美染色序列的算法了。我的代码中使用了MCS算法来求,论文中也有提到。可以看着代码来理解下这个算法,不过我的算法达不到论文中的时间复杂度O(m+n)。我用堆维护最大值,复杂度就比这点大,原来应该要用桶维护的。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x){
if(x<0){putchar('-');x=-x;}
if(x>=10) write(x/10);
putchar(x%10+'0');
}
void writeln(int x){
write(x);
puts("");
}
const int N=10005;
int n,m;
vector<int> e[N];
int R[N],SA[N],label[N]; //RA数组存完美消除序列中第几位是什么节点
priority_queue <pair<int,int> > heap; //默认大根堆
void Construct(){
fill(R+1,R+1+n,-1); //这里R数组是存节点在完美消除序列中是第几个
fill(label+1,label+1+n,0); //这里label数组是存节点的权值
for(int i=1;i<=n;i++)
heap.push(make_pair(0,i)); //将所有点都先加入heap中
for(int cnt=n;cnt>=1;){ //这里就是求完美消除序列的主体了(虽然之前的初始化也是)
int id=heap.top().second;
heap.pop();
if(R[id]!=-1) continue;
SA[cnt]=id; R[id]=cnt--;
for(int i=0;i<e[id].size();i++){
int u=e[id][i];
if(R[u]!=-1) continue;
label[u]++;
heap.push(make_pair(label[u],u));
}
}
}
void color(int u){
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(label[v]==-1) continue;
R[label[v]]=u;
}
for(int i=1;label[u]==-1;i++)
if(R[i]!=u) label[u]=i;
}
int color_graph(){ //这里label数组与R数组重新定义了
fill(label+1,label+n+1,-1); //label数组是用来存该节点染了什么颜色
fill(R+1,R+1+n,-1); //R数组是存当前节点周围是否有此颜色
for(int i=n;i>0;i--) color(SA[i]);
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,label[i]);
return ans;
}
int main(){
n=read(); m=read();
int u,v;
for(int i=1;i<=m;i++){
u=read(); v=read();
e[u].push_back(v);
e[v].push_back(u);
}
Construct();
write(color_graph());
return 0;
}