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();
	}
}



發佈了75 篇原創文章 · 獲贊 15 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章