題意:在一張無向圖中,問有多少個點不屬於任意一個奇圈,孤立點不屬於奇圈
首先明確兩個定理:
定理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();
}
}