题意:一些骑士要进行圆桌会议,每桌要坐奇数个人。互相憎恨的骑士不能相邻。现给出骑士之间的憎恨关系(双向),求删去多少人后,能正常开会。
解:将互相不憎恨的骑士连边,也就是补图。注意每个骑士一定不憎恨他自己(离散数学老师:和自己达成和解,这样挺好的)想想下学期就不能听他扯淡了还有点难过,一定不要连自环,在这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; }