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