UVA-Knights of the Round Table La3523

圆桌骑士;

分析:如果两个骑士没仇, 那就连一条边, 这样得到一个图G, 那么问题就转化成了求G中的不在任何一个简单奇圈上的点的个数, 我们可以求出在的个数再做减

又因为简单奇圈上的所有节点必定属于同一个双连通分量, 并且二分图没有奇圈, 所以问题就变成怎样判断一个双连通分量是否是奇圈见代码注释->

*
那么怎样判断一个双连通分量是奇圈呢?

首先我们要接受两条定理,

(1)如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),
那么这个双连通分量的其他顶点也在某个奇圈中;

(2)如果一个双连通分量含有奇圈,则他必定不是一个二分图。
反过来也成立,这是一个充要条件。

由于双连通分量也是一个图,那么要判断双连通分量是否为奇圈,只需判断这个双连通分量是否为一个二分图,
而要判断一个图是否为二分图,就用交叉染色法!

显然所有在奇圈中的骑士,都是允许出席会议的,而由于可能有部分骑士允许出席一个以上的会议(即他们是2个以上的奇圈的公共点),
那么为了避免重复统计人数,
当我们判断出哪些骑士允许出席会议时,就把他们做一个标记(相同的骑士只做一个标记)。
最后当Tarjan算法结束后,我们统计一下被标记的人数有多少,再用总人数减去这部分人,剩下的就是被亚瑟王剔除的人数了。
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define rep(i, j, k) for(int i=j; i<=k; ++i)
#define N 1005
#define mem(a, b) memset(a, b, sizeof(a))

int n, m, pre[N], low[N], _, res, top, stack[N];
int color[N], a[N];
bool map[N][N], can[N], b[N];

void init(){
	mem(pre, -1);mem(can, false);mem(map, true);
	_=res=top=0;
}
bool check(int u, int c){
	color[u]=c;
	rep(v, 1, n)	
		if(map[u][v] && b[v]){
			if(color[u]==color[v])return true;
			if(color[v]==-1)
				check(v, c^1);
		}
	return false;
}
void solve(int t, int *a){  
	int i;
	//cout<<t<<endl;
    memset(b,0,sizeof(b));  
    rep(j, 0, t-1){
        b[a[j]]=true;
		//cout<<a[i]<<"**"<<endl;
	}
    for(i=0; i<t; ++i){
        memset(color,-1,sizeof(color));  
        if(check(a[i], 1))  
            break;
    }  
    if(i<t)  
       rep(j, 0, t-1){  
            if(!can[a[j]]){  
                res++;  
                can[a[j]]=true;  
				//cout<<res<<"****"<<endl;
            }  
        }
}
void dfs(int u){
	low[u]=pre[u]=++_;
	stack[top]=u;
	top++;
	//cout<<top<<endl;
	rep(v, 1, n)
		if(map[u][v]){
			if(pre[v]==-1){
				dfs(v);
				low[u]=min(low[u],low[v]);
				if(low[v]>=pre[u]){
					int k=1;a[0]=u;
					do{
						a[k++]=stack[--top];
					}while(stack[top]!=v);
					solve(k, a);
				}
		}
		else low[u]=min(low[u],pre[v]);
	}
}

int main(){
	while(1){
		//read(n);read(m);
		scanf("%d%d", &n, &m);
		if((n+m)==0)break;
		init();
		rep(i, 1, m){
			int u, v;
			//read(u);read(v);
			scanf("%d%d", &u, &v);
			map[u][v]=map[v][u]=false;
		}
		rep(i, 1, n)map[i][i]=false;
		rep(i, 1, n)
			if(pre[i]<0)
				dfs(i);
		printf("%d\n", n-res);
	}
	return 0;
}
思路有了, 这题做起来其实也不是很复杂。
posted @ 2016-07-30 18:31  pbvrvnq  阅读(129)  评论(0编辑  收藏  举报