「题解」:[POJ2942]Knights of the Round Table
问题 E: Knights of the Round Table
时间限制: 1 Sec 内存限制: 256 MB
题面
题目描述
作为一名骑士是一个非常有吸引力的职业:寻找圣杯,拯救遇难的少女,与其他骑士一起喝酒是有趣的事情。因此,近年来亚瑟王国经历了骑士人数空前增加,这并不奇怪。现在有这么多骑士,每个圆桌骑士都可以同时来到卡米洛特并坐在圆桌旁;通常只有一小群骑士在那里,而其他人则忙于在全国各地做英雄事迹。
在讨论中,骑士很容易过度兴奋 - 特别是在喝了几杯酒之后。在发生一些不幸的事故之后,亚瑟王要求着名的精灵梅林确保将来不会在骑士之间爆发战斗。在仔细研究了这个问题之后,Merlin意识到只有骑士按照以下两条规则坐下才能防止战斗:
骑士应该坐着,这样两个相互仇恨的骑士不应该是桌子上的邻居。 (梅林有一个清单,上面写着谁讨厌谁。)骑士围坐在圆桌会议上,因此每个骑士都有两个邻居。
奇数骑士应坐在桌子周围。这确保了如果骑士无法就某事达成一致,那么他们可以通过投票解决问题。 (如果骑士的数量是偶数,则可以发生“是”和“否”具有相同的投票数,并且论证继续进行。)
只有满足这两条规则,梅林才会让骑士坐下来,否则他会取消会议。 (如果只有一个骑士出现,那么会议也会被取消,因为一个人不能坐在桌子旁。)Merlin意识到这意味着骑士不能参加任何符合这些规则的座位安排,并且这些骑士永远无法坐在圆桌会议上(如果一个骑士讨厌其他所有骑士,就会出现这种情况,但还有很多其他可能的原因)。如果骑士不能坐在圆桌会议上,那么他就不能成为圆桌骑士团的成员,必须从骑士团中被驱逐出去。这些骑士必须转移到一个不那么有声望的秩序,如方桌骑士团,八角桌骑士团或香蕉形桌骑士团。为了帮助Merlin,你必须编写一个程序来确定必须被驱逐的骑士数量。
输入格式
输入包含几组测试数据。 每种情况都以包含两个整数1≤n≤1000且1≤m≤1000000的整数行开始。 数字n是骑士的数量。 接下来的m行描述哪个骑士讨厌哪个骑士。 这m行中的每一行包含两个整数k1和k2,这意味着骑士数k1和骑士数k2彼此讨厌(数字k1和k2在1和n之间)。
输入由n = m = 0的块终止。
输出格式
对于每组测试数据,您必须在单独的行上输出一个整数:必须被驱逐的骑士数量。
题解
读题发现,题目要求坐在一起的骑士不能相互仇恨。
假如我们给每一对相互仇恨的骑士建边的话,我们需要找出相互之间没有连边的骑士。
这样似乎十分难处理。于是我们建补图(原先有边变无边,无边变有边)。
所以我们需要求出点双。
题目中还有这样一句话“奇数骑士应坐在桌子周围”,所以找奇环,用黑白染色法。(我染的点,听说有大神染的边)
主要要注意的是割点的处理。我们每次把属于该点双的全打上标记就可以了。如果在求点双里直接打标记的话,割点会被多个点双重复标记,处理起来很麻烦。
细节要处理好。
还有,多判要清空哦~
#include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<stack> #define rint register int using namespace std; struct node{int u,v,nxt;}edge[2000006],edgec[2000006]; int n,m,k1,k2,toty,firsty[1003],dfn[1003],firstc[1003]; int low[1003],cnt,root,newid[1003],totq,totc,c[1003],ans; int belong[1003],color[1003]; bool map[1003][1003],cut[1003],vis[1003]; stack <int> s;vector <int> dcc[1003]; inline void renew() { toty=cnt=root=totq=totc=ans=0; for(rint i=1;i<=n;++i) firsty[i]=dfn[i]=firstc[i]=low[i]=newid[i]=c[i]=belong[i]=0, color[i]=-1,cut[i]=vis[i]=false; for(rint i=1;i<=m;++i)edge[i].u=edge[i].v=edge[i].nxt=edgec[i].u=edgec[i].v=edgec[i].nxt=0; memset(map,false,sizeof(map)); } inline void build_line(int uu,int vv) { ++toty;edge[toty].u=uu;edge[toty].v=vv; edge[toty].nxt=firsty[uu];firsty[uu]=toty; } inline void add(int uu,int vv) { ++totc;edgec[totc].u=uu;edgec[totc].v=vv; edgec[totc].nxt=firstc[uu];firstc[uu]=totc; } inline void tarjan(int x) { dfn[x]=low[x]=++cnt;s.push(x); if(x==root && firsty[x]==0) { dcc[++totq].clear(); dcc[totq].push_back(x); return ; } int flag=0; for(rint i=firsty[x];i;i=edge[i].nxt) { int y=edge[i].v; if(!dfn[y]) { tarjan(y);low[x]=min(low[x],low[y]); if(low[y]>=dfn[x]) { flag++; if(x!=root||flag>1)cut[x]=true; totq++;int z; dcc[totq].clear(); do{ z=s.top();s.pop(); dcc[totq].push_back(z); }while(z!=y); dcc[totq].push_back(x); } } else low[x]=min(low[x],dfn[y]); } } inline bool dfs(int x,int last) { color[x]=color[last]^1; for(rint i=firsty[x];i;i=edge[i].nxt) { int y=edge[i].v; if(!belong[y]||y==last)continue; if(color[y]!=-1&&color[y]==color[x])return true; if(color[y]==-1&&dfs(y,color[x]))return true; } return false; } int main() { while(1) { scanf("%d %d",&n,&m); if(n==0&&m==0)return 0; renew(); for(rint i=1;i<=m;++i) { scanf("%d %d",&k1,&k2); map[k1][k2]=map[k2][k1]=1; } for(rint i=1;i<=n;++i) for(rint j=i+1;j<=n;++j) if(!map[i][j]) build_line(i,j),build_line(j,i); for(rint i=1;i<=n;++i) if(!dfn[i])root=i,tarjan(i); color[0]=0; for(rint i=1;i<=totq;++i) { for(rint j=0;j<dcc[i].size();++j) belong[dcc[i][j]]=1; if(dfs(dcc[i][0],0)) for(rint j=0;j<dcc[i].size();++j) vis[dcc[i][j]]=true; for(rint j=0;j<dcc[i].size();++j) { if(cut[dcc[i][j]]) color[dcc[i][j]]=-1; belong[dcc[i][j]]=0; } } for(rint i=1;i<=n;++i) if(vis[i])ans++; ans=n-ans;printf("%d\n",ans); } return 0; }