POJ 2942 Knights of the Round Table(双连通分量+二分图)
http://poj.org/problem?id=2942
题意: 亚瑟王要在圆桌上召开骑士会议,为了不引发骑士之间的冲突,并且能够让会议的议题有令人满意的结果,每次开会前都必须对出席会议的骑士有如下要求:
1、 相互憎恨的两个骑士不能坐在直接相邻的2个位置;
2、 出席会议的骑士数必须是奇数,这是为了让投票表决议题时都能有结果。
如果出现有某些骑士无法出席所有会议(例如这个骑士憎恨所有的其他骑士),则亚瑟王为了世界和平会强制把他剔除出骑士团。
现在给定准备去开会的骑士数n,再给出m对憎恨对(表示某2个骑士之间使互相憎恨的),问亚瑟王至少要剔除多少个骑士才能顺利召开会议?
注意:1、所给出的憎恨关系一定是双向的,不存在单向憎恨关系。
2、由于是圆桌会议,则每个出席的骑士身边必定刚好有2个骑士。即每个骑士的座位两边都必定各有一个骑士。
3、一个骑士无法开会,就是说至少有3个骑士才可能开会。
分析:本题在刘汝佳训练指南P316有详解.
首先以骑士为节点建立无向图,如果任意两个骑士不互相憎恨,那么就在他们之间连一条无向边.现在问题转换成了有多少骑士不在任意一个奇圈里面?
简单圈上的所有节点必然在同一个(点)双连通分量上.所以我们只需要处理每个(点)双连通分量,看看该双连通分量中是否有奇圈,就可以判断该分量中的点是否在一个奇圈上.我们用odd[i]=1表示i号节点在一个奇圈上.
二分图是没有奇圈的,那么非二分图的双连通分量是不是所有定点都在一个奇圈上呢?首先非二分图的双连通分量肯定存在至少一个奇圈(否则它就是二分的).我们可以证明非二分图的双连通分量的所有顶点都可以被划分到一个奇圈里去.所以这些点都要标记odd[i]=1.(该证明过程请见刘汝佳 训练指南 P317)
综上所述:我们对于每个双连通分量,若它不是二分图,就标记它的所有顶点都在奇圈上(odd[i]=1),最后我们输出的结果就是odd[i]=0的那些点.(注意:割顶属于多个双连通分量,可能被标记多次)
AC代码:
#include<cstdio> #include<cstring> #include<vector> #include<stack> #include<algorithm> using namespace std; const int maxn=1000+10; int n,m; int bcc_cnt,dfs_clock; struct Edge { int u,v; Edge(int u,int v):u(u),v(v){} }; vector<int> G[maxn],bcc[maxn]; stack<Edge> S; int pre[maxn],bccno[maxn],iscut[maxn]; int dfs(int u,int fa) { int lowu=pre[u]=++dfs_clock; int child=0; for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(v==fa) continue; Edge e=Edge(u,v); if(!pre[v]) { S.push(e); child++; int lowv=dfs(v,u); lowu=min(lowu,lowv); if(lowv>=pre[u]) { iscut[u]=true; bcc_cnt++; //从1编号 bcc[bcc_cnt].clear(); while(true) { Edge x=S.top(); S.pop(); if(bccno[x.u]!=bcc_cnt) { bcc[bcc_cnt].push_back(x.u), bccno[x.u]=bcc_cnt; } if(bccno[x.v]!=bcc_cnt) { bcc[bcc_cnt].push_back(x.v), bccno[x.v]=bcc_cnt; } if(x.u==u && x.v==v) break; } } } else if(pre[v]<pre[u]) S.push(e), lowu=min(lowu,pre[v]); } if(fa<0 && child==1) iscut[u]=false; return lowu; } void find_bcc(int n) { memset(pre,0,sizeof(pre)); memset(bccno,0,sizeof(bccno)); memset(iscut,0,sizeof(iscut)); dfs_clock=bcc_cnt=0; for(int i=0;i<n;i++) if(!pre[i]) dfs(i,-1); } int color[maxn],odd[maxn]; bool bipartite(int u,int b) { for(int i=0;i<G[u].size();i++) { int v=G[u][i]; if(bccno[v]!=b) continue; if(color[v]==color[u]) return false; else if(!color[v]) { color[v]=3-color[u]; if(!bipartite(v,b)) return false; } } return true; } int A[maxn][maxn]; int main() { while(scanf("%d%d",&n,&m)==2&&n) { memset(A,0,sizeof(A)); for(int i=0;i<n;i++) G[i].clear(); for(int i=0;i<m;i++) { int u,v; scanf("%d%d",&u,&v); u--; v--; A[u][v]=A[v][u]=1; } for(int i=0;i<n;i++) for(int j=i+1;j<n;j++)if(!A[i][j]) { G[i].push_back(j); G[j].push_back(i); } find_bcc(n); memset(odd,0,sizeof(odd)); for(int i=1;i<=bcc_cnt;i++) { memset(color,0,sizeof(color)); for(int j=0;j<bcc[i].size();j++) bccno[bcc[i][j]]=i; int u=bcc[i][0]; color[u]=1; if(!bipartite(u,i)) for(int j=0;j<bcc[i].size();j++) odd[bcc[i][j]]=1; } int ans=n; for(int i=0;i<n;i++)if(odd[i]) ans--; printf("%d\n",ans); } return 0; }