【POI 2007】Office 办公楼(BIU)

  http://www.zybbs.org/JudgeOnline/problem.php?id=1098

  题目大意:给定一个N个点M条边的无向图,将N个点分成尽量多的组,满足任意两个不在同一组的点之间都有边。

  此题的答案就是原图补图的联通块个数。具体在WC2011 MT的课件中有。

  原话是:将未访问点挂链,扩展一个点时,标记原图中该点的相邻点,遍历链表,将所有未标记点(即该点在补图中的邻居)入队并从链表中删除。

  然后我就根据这个很笨拙地串了一条链……

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#include <algorithm>
#define mm 2000100
#define mn 100003
using namespace std;

queue<int> q;
vector<int> v;
int n,m,a,b,ans;
bool vis[mn];
struct EDGE{
	int pnt;
	EDGE *pre;
	EDGE(){}
	EDGE(int _pnt,EDGE *_pre):pnt(_pnt),pre(_pre){}
}Edge[mm*2],*SP=Edge,*edge[mm];

struct LINE{
	int pre,next;
	bool flag;	
}line[mn];

inline void addedge(int a,int b){
	edge[a]=new(++SP)EDGE(b,edge[a]);
	edge[b]=new(++SP)EDGE(a,edge[b]);
}

void Del(int i){
	line[line[i].next].pre=line[i].pre;
	line[line[i].pre].next=line[i].next;
}

void bfs(){
	while(line[0].next!=-1){
		q.push(line[0].next);
		vis[line[0].next]=true;
		Del(line[0].next);
		int cnt=1;
		while(!q.empty()){
			int i=q.front();q.pop();
			for(EDGE *j=edge[i];j;j=j->pre) line[j->pnt].flag=true;
			for(int j=line[0].next;j>0;j=line[j].next)
				if(!line[j].flag && !vis[j]){
					q.push(j);
					vis[j]=true;
					Del(j);
					cnt++;
				}
			for(int j=line[0].next;j>0;j=line[j].next) line[j].flag=false;
		}
		ans++;
		v.push_back(cnt);
	}
}							

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d%d",&a,&b);
		addedge(a,b);
	}
	for(int i=1;i<=n;i++) 
		line[i-1].next=i,line[i].pre=i-1;
	line[n].next=-1;
	
	bfs();
	sort(v.begin(),v.end());
	
	printf("%d\n",ans);
	if(!v.empty()){
		for(int i=0;i<v.size()-1;i++) printf("%d ",v[i]);
		printf("%d\n",v[v.size()-1]);
	}

	return 0;
}



原文地址:https://www.cnblogs.com/Delostik/p/2132463.html