圆桌骑士;
分析:如果两个骑士没仇, 那就连一条边, 这样得到一个图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;
}
思路有了, 这题做起来其实也不是很复杂。