标签: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