标签:acm algorithm poj tarjan graph
Time Limit: 7000MS | Memory Limit: 65536K | |
Total Submissions: 10908 | Accepted: 3585 |
Description
Input
Output
Sample Input
5 5 1 4 1 5 2 5 3 4 4 5 0 0
Sample Output
2
Hint
题意:一些骑士,他们之间有互相讨厌的关系,讨厌的两人不能相邻坐,每次开会都要有奇数个骑士坐在圆桌,永远不能坐进圆桌的骑士将会被剔除,问剔除的骑士有多少个。
分析:此题完全是按照刘汝佳的大白书《算法竞赛入门经典——训练指南》写的。简单讲讲做法。首先,将互不讨厌的关系都用边连接起来,建立无向图G。题目就转化为求不在任意一个“简单奇圈”上的节点个数。一个简单圈上的所有节点必然属于同一个双连通分量,而由于双连通分量中如果存在奇环,那么它的每一个点都会在奇环上,那么判断该双连通分量如果存在奇环,则该连通分量上的点都不要删掉。而二分图是不存在奇环的,所以判断是否存在奇环,只需判断该连通分量是否为二分图。要注意的是,由于一个点可能属于多个双连通分量,所以处理的时候不能直接加双连通分量点的个数,而是每次找到一个存在奇环的双连通分量时,先标记不要剔除的点,最后再统计需要剔除的点。
点的双连通分量用tarjan求,判断二分图用交叉染色法判定。
题目链接:http://poj.org/problem?id=2942
代码清单:
#include<set> #include<map> #include<cmath> #include<queue> #include<stack> #include<ctime> #include<cctype> #include<string> #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; typedef unsigned int uint; typedef unsigned long long ull; const int maxn = 2000 + 5; const int maxv = 4000000 + 5; struct node{ int v,next; }graph[maxv]; struct Edge{ int u,v; Edge(){} Edge(int u,int v){ this -> u = u; this -> v = v; } }; int n,m,a,b; bool tmap[maxn][maxn]; int dfn[maxn]; int low[maxn]; int belong[maxn]; int head[maxn]; int num,idx,sccno; stack<Edge>sta; vector<int>bcc; int color[maxn]; bool odd[maxn]; void init(){ memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(head,-1,sizeof(head)); memset(tmap,false,sizeof(tmap)); memset(odd,false,sizeof(odd)); while(!sta.empty()) sta.pop(); idx=0; sccno=0; num=0; } void add(int u,int v){ graph[num].v=v; graph[num].next=head[u]; head[u]=num++; } void input(){ for(int i=1;i<=m;i++){ scanf("%d%d",&a,&b); tmap[a][b]=tmap[b][a]=true; } } void get_graph(){ for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(!tmap[i][j]){ add(i,j);add(j,i); } } bool bipartite(int u,int colors){ color[u]=colors; for(int i=head[u];i!=-1;i=graph[i].next){ int v=graph[i].v; if(!belong[v]) continue; if(color[v]==color[u]) return false; if(color[v]==0){ if(!bipartite(v,3-colors)) return false; } }return true; } void tarjan(int u,int father){ low[u]=dfn[u]=++idx; for(int i=head[u];i!=-1;i=graph[i].next){ int v=graph[i].v; if(v==father) continue; if(!dfn[v]){ //v未标记,则(u,v)为树边 sta.push(Edge(u,v)); //边入栈 tarjan(v,u); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]){ //子节点不能到比u更早的节点,则u是割点 sccno++; memset(belong,0,sizeof(belong)); memset(color,0,sizeof(color)); bcc.clear(); while(!sta.empty()){ //把这个双连通分量的点取出来 Edge e=sta.top(); sta.pop(); bcc.push_back(e.u); belong[e.u]=sccno; bcc.push_back(e.v); belong[e.v]=sccno; if(e.u==u&&e.v==v) break; } if(!bipartite(bcc[0],1)){ //如果不是二分图 for(int j=0;j<bcc.size();j++) odd[bcc[j]]=true; } } } else if(dfn[v]<dfn[u]){ //回边 sta.push(Edge(u,v)); //边入栈 low[u]=min(low[u],dfn[v]); } } } void find_scc(){ for(int i=1;i<=n;i++){ if(!dfn[i]) tarjan(i,-1); } } int get_ans(){ int ans=n; get_graph(); find_scc(); for(int i=1;i<=n;i++) if(odd[i]) ans--; return ans; } void solve(){ printf("%d\n",get_ans()); } int main(){ while(scanf("%d%d",&n,&m)!=EOF&&n&&m){ init(); input(); solve(); } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ_2942_Knights of the Round Table(点的双连通分量+二分图判定)
标签:acm algorithm poj tarjan graph
原文地址:http://blog.csdn.net/jhgkjhg_ugtdk77/article/details/47429725