题目链接:http://poj.org/problem?id=2942
题目大意:
将若干个骑士分配到若干个桌子开会,
每个桌子的人数都是奇数。
若两个骑士相互憎恨,那么他们不能左右相邻坐。
如果一个骑士无法被安排在任何一个桌子,那么他将会被驱逐。
输出至少驱逐几个骑士。
算法:
双联通+奇圈。
首先,如果两个骑士可以相邻坐,就在它们之间连一条无向边。
然后求双联通分量。求双联通分量的方法可以参照BYVoid的《图的割点、桥与双连通分支》
对于每个双联通分量,其实实质上就是一个环(原路径+替代路径=环)。
现在有一个性质,如果一个双联通分量中含有一个奇环,那么这个双联通的所有点都位于奇数环中。
证明也很容易:
奇环从任两点都可以拆成两条路径,一条点数为奇数,一条点数为偶数。
这个双联通分支上的任何一点一定可以到达这个奇环上的任两点,
那么根据这个点到达这两个点的路径长的奇偶性,选择某一天路径拼接起来就可以了。
所以现在只要判断每个双联通分量是否含有奇环就可以了。
要判断块是否含有奇环,是不能通过点数判断的。
因为偶数点的块可以含有奇环:
奇数点的块也可以不含奇环:
一个图含有奇环的充要条件是它不是一个二分图。
判定二分图时,使用的是黑白染色法。
所以我们可以用判定二分图的方法判定奇圈。
当然,不含奇圈的联通块和单点的联通块就是必须驱逐的骑士了。
代码如下:
#include<cstdio> #include<cstring> #include<stack> #include<queue> #define MIN(x,y) ((x)<(y))?(x):(y) using namespace std; int E,n,head[1100]; bool flg[1100],map[1100][1100],vis[1100]; int dfs[1100],low[1100],clr[1100],p[1100],tot; stack<int>ss; struct { int u,v,nxt; } edge[2100000]; void addedge(int u,int v) { edge[E].u=u; edge[E].v=v; edge[E].nxt=head[u]; head[u]=E++; edge[E].u=v; edge[E].v=u; edge[E].nxt=head[v]; head[v]=E++; } bool judge(int end) { memset(clr,-1,sizeof(clr)); ss.pop(); while(!ss.empty()) { int i=ss.top(); ss.pop(); clr[edge[i].u]=clr[edge[i].v]=0; if(i==end) break; } clr[edge[end].u]=1; queue<int>qq; qq.push(edge[end].u); while(!qq.empty()) { int u=qq.front(); qq.pop(); for(int i=head[u]; i!=-1; i=edge[i].nxt) { int v=edge[i].v; if(clr[v]==-1)continue; else if(clr[v]==clr[u])return true; else if(!clr[v]) { clr[v]=clr[u]%2+1; qq.push(v); } } } return false; } void dfst(int u) { low[u]=dfs[u]=tot++; for(int i=head[u]; i!=-1; i=edge[i].nxt) { int v=edge[i].v; if(dfs[v]==-1) { ss.push(i); p[v]=u; dfst(v); low[u]=MIN(low[u],low[v]); if(p[u]!=-1) { if(dfs[u]<=low[v]&&judge(i)) { for(int j=0; j<n; j++) { if(clr[j]!=-1) { flg[j]=true; } } } } else { int j=head[u]; int pre=edge[j].v; for(; j!=-1; j=edge[j].nxt) { if(edge[j].v!=pre) break; } if(j!=-1&&judge(i)) { for(int k=0; k<n; k++) { if(clr[k]!=-1) { flg[k]=true; } } } } } else { if(p[u]!=v) low[u]=MIN(low[u],dfs[v]); if(dfs[v]<dfs[u]) ss.push(i); } } } int main() { int m; while(~scanf("%d%d",&n,&m)&&(n||m)) { memset(map,0,sizeof(map)); memset(head,-1,sizeof(head)); memset(flg,0,sizeof(flg)); memset(dfs,-1,sizeof(dfs)); E=0; while(m--) { int u,v; scanf("%d%d",&u,&v); u--; v--; map[u][v]=map[v][u]=1; } for(int u=0; u<n; u++) for(int v=u+1; v<n; v++) if(!map[u][v]) addedge(u,v); for(int u=0; u<n; u++) { if(dfs[u]!=-1)continue; while(!ss.empty()) ss.pop(); tot=0; p[u]=-1; dfst(u); } int ans=0; for(int i=0; i<n; i++) if(!flg[i])ans++; printf("%d\n",ans); } return 0; }