poj 2942 Knights of the Round Table

题意:在一张无向图中,问有多少个点不属于任意一个奇圈,孤立点不属于奇圈

首先明确两个定理:

定理1:对于一个点双连通分量,如果找个一个奇圈那么这个分量的其他点也必然在某个奇圈内。

   证明很简单,设ab是一个奇圈上的点,c不属于这个奇圈,设c到a和边数为m到b的边数为n,若m+n为奇数,则c位于由这个奇环除ab外的其他边(偶数条)构成一个奇环,如果m+n为偶数,那么c到ab的路径与ab边构成一个奇环。次定理仅适用于点双连通分量,不适用于边连通分量。

1           4

| \       /   \                 

|   \   /       \

|     2          5            此图在很多时候被用到,尤其是不太确定应该使用边连通还是点连通时。

|   /   \       /

| /       \   /

3          6

依次此题变为判断一个点连通分量里是否存在一个奇圈,有事又用到了定理2

定理2:一个图是二部图当且仅当它不包含奇圈。这是判定二部图的一个方法,并一个方法时染色,因此我们可以染色的方法盘奇圈,如果一个连通分量中某边的两点染色相同则不是二部图,即存在奇圈,则此连通分量中所有点都不会被去除。

另外一种判断二分图的方法是使用并查集,两点有边说明两点不属于一类,每次加边时用差统计量判断是否矛盾即可,效率应该和染色差不多,但是更好写,sgu172 eXam二分图判定

PS:此题很经典也很综合,目前的瓶颈在于想不到那俩定理。。。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;

public class Main{
	int maxn = 1010,maxm=2000010;
	class node{
		int be,ne;
		node(int b,int e){
			be=b;
			ne=e;
		}
	}
	class block{
		node buf[]=new node[maxn];
		int E[]=new int[maxn],len;
		int cut;
		void  init(int c)
		{
			cut=c;
			Arrays.fill(E,-1);
			len=0;
		}
		void add(int a,int b)
		{
			buf[len]=new node(b,E[a]);
			E[a]=len++;
		}
		int color[]=new int[maxn];
		void work(){
			Arrays.fill(color,-1);
			if(tocolor(cut,0))
				return;
			flag[cut]=1;
			for(int i=0;i<len;i++)
				flag[buf[i].be]=1;
		}
		boolean tocolor(int a,int c){
			boolean res=true;
			color[a] = c;
			for (int i=E[a];i!=-1;i=buf[i].ne) {
				int b = buf[i].be;
					if (color[b] == -1)
						res&=tocolor(b, c ^ 1);
					if (color[b] == c)
						return false;
			}
			return res;
		}		
	}
	
	node buf[]=new node[maxm];
	int E[]=new int[maxn],len;
	void add(int a,int b){
		buf[len]=new node(b,E[a]);
		E[a]=len++;
		buf[len]=new node(a,E[b]);
		E[b]=len++;
	}
	void init(){
		Arrays.fill(E,-1);
		len=0;
	}
	int map[][] = new int[maxn][maxn], n;
	int dfn[] = new int[maxn], low[] = new int[maxn], cnt;
	int stack[] = new int[maxn], top;
	int flag[] = new int[maxn];
	block bk=new block();
	void dfs(int a) {
		dfn[a] = low[a] = ++cnt;	
		for (int i=E[a];i!=-1;i=buf[i].ne) {
			int b=buf[i].be;
			if (dfn[b] == 0) {	
				stack[++top] = i;		
				dfs(b);
				if (low[b] < low[a])
					low[a] = low[b];
				if (low[b] >= dfn[a]) {
					int x;
					bk.init(a);
					do {
						x = stack[top--];
						bk.add(buf[x^1].be, buf[x].be);
					} while (buf[x^1].be!=a||buf[x].be!= b);// a组成多个块,不能弹出
					bk.work();
				}
			} else if (dfn[b] < low[a]){
				low[a] = dfn[b];
				stack[++top] = i;
				}
		}
	}

	int solve() {
		for (int i = 1; i <= n; i++)
			dfn[i] = low[i] = flag[i] = 0;
		top = cnt = 0;
		for(int i=1;i<=n;i++)
			if(dfn[i]==0)
		         dfs(i);
		int ans = 0;
		for (int i = 1; i <= n; i++)
		if (flag[i] == 0)
				ans++;
		return ans;
	}

	StreamTokenizer in = new StreamTokenizer(new BufferedReader(
			new InputStreamReader(System.in)));

	int nextInt() throws IOException {
		in.nextToken();
		return (int) in.nval;
	}

	void run() throws IOException {
		while(true){
		n = nextInt();
		int m = nextInt();
		if(n==0)
			break;
		for (int i = 1; i <= n; i++)
			for (int j = i + 1; j <= n; j++)
				map[i][j] = map[j][i] = 1;
		int a, b;
		while (m-- > 0) {
			a = nextInt();
			b = nextInt();
			map[a][b] = map[b][a] = 0;
		}
		init();
		for (int i = 1; i <= n; i++)
			for (int j = i + 1; j <= n; j++)
				if(map[i][j]==1)
					add(i,j);
		System.out.println(solve());
		}
	}

	public static void main(String[] args) throws IOException {
		new Main().run();
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值