1. 程式人生 > 其它 >POJ-2942 Knights of the Round Table

POJ-2942 Knights of the Round Table

tarjan找環+判奇環

題意:一些騎士要進行圓桌會議,每桌要坐奇數個人。互相憎恨的騎士不能相鄰。現給出騎士之間的憎恨關係(雙向),求刪去多少人後,能正常開會。

解:將互相不憎恨的騎士連邊,也就是補圖。注意每個騎士一定不憎恨他自己(離散數學老師:和自己達成和解,這樣挺好的)想想下學期就不能聽他扯淡了還有點難過,一定不要連自環,在這WA了好幾次。因為要坐成一個圓,所以在圖上找大於等於3的環。找環用tarjan,但找到了環不能確定它的大小,因為可以偶數環裡套奇數環,所以還要判斷一下這些點裡有沒有奇數環,可以用染色,如果一個點將染兩種不同的顏色,那就有奇環了。二分圖判斷也是用的這個方法。

現在來具體實現。首先無向圖tarjan找環和有向圖是不同的,因為會找到父親那裡,所以要多傳一個引數,特判一下(注意這道題沒有重邊,所以可以直接判)。找環的第一個點,也就是割點的判斷條件,dfn[now]<=low[to],然後從棧裡把這個環拿出來,判一下奇偶。第一個點不要彈出棧,因為還要找下一條邊。

程式碼:

#include <algorithm>
#include <stack>
#include <vector>
#include <stdio.h>
using namespace std;
#define maxx 10005
#define maxn 1005
#define maxm 5000005
#define inf 0x3f3f3f3f
int n,m;
vector<int> e[maxn];
int dfn[maxn]={0},low[maxn]={0};
int cnt=0,vis[maxn]={0};
int cir[maxn],is
[maxn]; int bel[maxn],color[maxn],belnum; int mp[maxn][maxn]; stack<int> s; int dfs(int now,int col){ color[now]=col; for(int i=0;i<e[now].size();i++){ int to=e[now][i]; if(bel[to]!=bel[now]) continue; if(!color[to]&&dfs(to,-col))
return 1; if(color[to]==col) return 1; } return 0; } void tarjan(int now,int fa){ s.push(now); vis[now]=1; low[now]=dfn[now]=++cnt; for(int i=0;i<e[now].size();i++){ int to=e[now][i]; if(to==fa) continue; if(!dfn[to]){ tarjan(to,now); low[now]=min(low[now],low[to]); if(dfn[now]<=low[to]){ int num=0; belnum++; while(1){ int t=s.top(); s.pop(); vis[t]=0; bel[t]=belnum; cir[++num]=t; if(t==to) break; } cir[++num]=now; bel[now]=belnum; memset(color,0,sizeof color); if(num>=3&&dfs(now,1)){ for(int j=1;j<=num;j++) is[cir[j]]=1; } } } else if(vis[to]) low[now]=min(low[now],dfn[to]); } } void init(){ memset(dfn,0,sizeof dfn); memset(low,0,sizeof low); memset(vis,0,sizeof vis); memset(e,0,sizeof e); memset(mp,0,sizeof mp); memset(is,0,sizeof is); memset(bel,0,sizeof bel); cnt=0;belnum=0; while(!s.empty()) s.pop(); } signed main() { while(~scanf("%d%d",&n,&m)){ if(n==0&&m==0) break; init(); for(int i=0;i<m;i++){ int x,y; scanf("%d%d",&x,&y); mp[x][y]=1; mp[y][x]=1; } for(int i=1;i<=n;i++) { for (int j = 1; j <= n; j++) if (!mp[i][j]&&i!=j) e[i].push_back(j); } for(int i=1;i<=n;i++){ if(!dfn[i]) tarjan(i,0); } int ans=0; for(int i=1;i<=n;i++){ if(is[i]) ans++; } ans=n-ans; printf("%d\n",ans); } return 0; }
View Code