您的位置:首页 > 其它

poj 2942 (点双联通+判断二分图)

2013-09-24 13:16 495 查看


poj 2942 (点双联通+判断二分图)

分类: 强连通—双连通—LCA—2-SAT2013-09-23
17:58 80人阅读 评论(0) 收藏 举报

ACM编程算法百度Tarjan


如何判断一个图中是否存在回路

2011-12-30 20:22:50| 分类: 算法 | 标签:图中回路问题 bfs dfs 圈的个数 拓扑排序 |字号 订阅

问题描述:给一个图G=<V,E>,问如何判断这个图中是否存在回路?请给出至少3中方法
分析:
方法1:利用减枝的方法,如果G为有向图:
1)首先删除入读为0的点,并且将对应的和该点相连的点的入读-1。(可以用一个数组表示节点被删除的状态)
2)重复过程1),直到没有入读为0的点,如果还有没被删除的节点,则该有向图一定存在回路
如果G为无向图:
1)首先删除所有度数<=1的点,然后将与这些点相连的所有点的度数-1,然后将所有度数为1的点加入队列中
2)对队列中的每个点,重复过程1),如果还有没被删除的节点,那么证明该图一定存在回路。
方法2:(有向图)利用拓扑排序
1)首先利用DFS进行拓扑排序,最后生成一个拓扑序链表,然后为每个节点设置一个是否被访问过标记,用Visit数组
2) 遍历这个链表,对每个节点v,设置visit[v]=1,如果判断如果存在与该节点相邻的节点u,使得Visit[u]=1,那么证明存在回边,这图中一定存在圈。

方法3:(无向图而言)利用BFS(利用算法导论上BFS的版本,每个节点有一个color属性,标记节点的颜色:“白”、“灰”、“黑”)
1)直接利用BFS进行遍历,在判断当前节点的相邻的节点时,附加一个判断:如果这个节点的颜色为“灰色”,则return false
2)遍历完所有的节点,返回true

方法4:(无向图而言)还是利用BFS,在遍历过程中,为每个节点标记一个深度deep[],如果存在某个节点为v,除了其父节点u外,还存在与v相邻的节点w使得deep[v]<=deep[w]的,那么该图一定存在回路。

方法5:(无向图而言)用BFS或DFS遍历,最后判断对于每一个连通分量当中,如果边数m>=节点个数n,那么改图一定存在回路。因此在DFS或BFS中,我们可以统计每一个连通分量的顶点数目n和边数m,如果m>=n则return false;直到访问完所有的节点,return true.

问题扩展:如何求出一个图中所有简单回路的个数,并打印出这些简单回路?


如何判断一个图是否是二分的?

2011-12-30 17:40:46| 分类: 算法 | 标签:二分图判定 bfs |字号 订阅

问题描述:给定一个无向图G=<V,E>,如何能够在O(V+E)时间内判断这个图是否是一个二分图(或者称为2着色)

分析:如果这个无向图没有回路,那么它一定是可以二分的,我们直接就可以利用BFS来为二分图着色(根据当前节点的颜色,对其所有相邻节点涂另一个颜色)。
如果这个无向图有回路,如果回路的边数是偶偶数,那么它还是二分的,如果是奇数,那么它就不是二分的。如何判断回路的边数是奇数还是偶数呢?
方法1:利用DFS记录初始节点到每个节点的距离d[],初始节点为s,假设当前访问节点为v,如图:



c是从v到s和从u道s的交叉点,c当然可以和s相同,如果当前节点v相邻的节点u已经被标记为灰色(即正在栈中、或递归调用还没返回),那么这个图就是存在环路的,如何判断当前环路的边是否是奇数呢,不是一般性,假设d2>d3,那么我们只需要判断d2-d3+1是否为奇数,如果是奇数,则return false,证明当前图不是二分,如果是偶数则继续遍历,直到所有点均已遍历完毕,则返回true。
d2-d3正是从v到c和从u到c距离的差值,环c->....->v->u->...->c的变长可以表示为N=2*d3+d2-d3+1,所以N的奇偶性完全由d2-d3+1决定。当然在DFS遍历的时候也可能出现如下情形:



当前访问的节点v,与它相邻的节点u已经是灰色,那么按照刚才的分析,color[u]="Gray",但中不一定构成回路,但我们的判断d2-d1+1依然正确,因为这种情况下d2-d1+1=2,一定是偶数
时间代价O(V+E),空间O(1)(不考虑本身图所占用的结构开销)

方法2:我们还可以利用类似减枝的方法来判断图中的回路是否为偶数,基本思想是这样的,如果一个无向图中存在环路,那么在这个环路中每个节点的度数>=2,。
1,首先,我们需要统计每个节点的度数,记录在degree
中 O(V+E)
2,删除所有度数<=1的节点(删除操作就是将该节点对应的边删除,并将对应的顶点的度数-1),然后将所有更新后的顶点度数为1的点加入队列当中。O(V)
3,然后对队列中的每个元素重复步骤1,最后如果还有没有被删除的节点(即还存在度数>=2的节点),证明图中存在回路。如果全部都已删除则图中不存在回路,即图是二分的
4,(上面的过程看做预处理过程)如果存在回路,接下来,对剩下的每一个节点,统计从这个节点开始的所有回路(即用DFS从这一点开始,又回到这一点的所有回路),并记录其路径的长度,并标记被访问的状态,如果存在偶数边则return false。
5,如果存在还没有被访问的点(另一个连通分支的回路),继续步骤4,知道所有节点都被访问完毕,return true.
方法2的复杂度要高一些,应该有一些可以优化的地方。

题意:亚瑟王要在圆桌上召开骑士会议,为了不引发骑士之间的冲突,并且能够让会议的议题有令人满意的结果,每次开会前都必须对出席会议的骑士有 如下要求:1:相互憎恨的两个骑士不能坐在直接相邻的2个位置;2:出席会议的骑士数必须是奇数,这是为了让投票表决议题时都能有结果。

现在给定准备去开会的骑士数n,再给出m对憎恨关系(表示某2个骑士之间是互相憎恨的),问有多少骑士不管跟哪些骑士都无法组成圆桌会议?

思路:没有矛盾的骑士之间建边,能组成圆桌会议的肯定形成一个环,并且环的点数为奇数。可以用Tarjan求出点双联通分量,判断每个联通分量是否存在 奇环,如果存在奇环的话,该联通分量的所有节点都可以参加会议。一个图是二分图是不存在奇环的充要条件,所以只需要判断该联通分量是否是二分图
即可。

[cpp] view
plaincopy

#include<stdio.h>

#include<stack>

#include<math.h>

#include<string.h>

const int N=1010;

using namespace std;

int cnt
,low
,dfs
,head
,num,ans,idx,first
,color
,flag,n,eenum;

bool map

,vis
;

struct edge

{

int st,ed,next;

}e[N*N],ee[N*N];

void addedge(int x,int y)

{

e[num].st=x;e[num].ed=y;e[num].next=head[x];head[x]=num++;

e[num].st=y;e[num].ed=x;e[num].next=head[y];head[y]=num++;

}

void Addedge(int x,int y)

{

ee[eenum].st=x;ee[eenum].ed=y;ee[eenum].next=first[x];first[x]=eenum++;

ee[eenum].st=y;ee[eenum].ed=x;ee[eenum].next=first[y];first[y]=eenum++;

}

bool Judge(int u,int cor)//判断二分图

{

color[u]=cor;

int i,v;

for(i=first[u];i!=-1;i=ee[i].next)

{

v=ee[i].ed;

if(color[v]==-1)

{

return Judge(v,cor^1);

}

else if(color[v]==color[u])return true;//不是二分图

}

return false;

}

stack<int>Q;

void Tarjan(int u,int id)

{

int v,i,temp,j;

dfs[u]=low[u]=idx++;

for(i=head[u];i!=-1;i=e[i].next)

{

v=e[i].ed;

if(i==(id^1))continue;

if(dfs[v]==-1)

{

Q.push(i);

Tarjan(v,i);

low[u]=low[u]>low[v]?low[v]:low[u];

if(dfs[u]<=low[v])//u为割点

{

memset(first,-1,sizeof(first));

memset(color,-1,sizeof(color));

eenum=0;

do

{

temp=Q.top();

Q.pop();

Addedge(e[temp].st,e[temp].ed);

}while(temp!=i);//一个双联通分量的所有边

if(Judge(u,0))//如果不是二分图该联通分量里所有点都可以参加会议

{

for(j=0;j<eenum;j+=2)

vis[ee[j].st]=vis[ee[j].ed]=true;

}

}

}

else if(low[u]>dfs[v])

{

Q.push(i);

low[u]=dfs[v];

}

}

}

int main()

{

int m,x,y,i,j,sum,Case=0;

while(scanf("%d%d",&n,&m)!=-1&&n+m)

{

memset(head,-1,sizeof(head));

memset(map,false,sizeof(map));

num=0;

for(i=0;i<m;i++)

{

scanf("%d%d",&x,&y);

map[x][y]=map[y][x]=true;

}

for(i=1;i<=n;i++)

{

for(j=i+1;j<=n;j++)

if(map[i][j]==false)

addedge(i,j);

}

memset(dfs,-1,sizeof(dfs));

memset(vis,false,sizeof(vis));

ans=idx=0;sum=0;

for(i=1;i<=n;i++)//图可能不连通

{

if(dfs[i]==-1)

Tarjan(i,-1);

}

for(i=1;i<=n;i++)

if(!vis[i])

sum++;

printf("%d\n",sum);

}

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: