您的位置:首页 > 其它

强连通相关:poj1236,poj2186,poj2762,hdu4738

2015-09-04 20:25 411 查看
相关的概念:
割边:在连通图中,删除了连通图的某条边后,图不再连通。这样的边被称为割边,也叫做桥。

割点:在连通图中,删除了连通图的某个点以及与这个点相连的边后,图不再连通。这样的点被称为割点。

对于有向图上的2个点a,b,若存在一条从a到b的路径,也存在一条从b到a的路径,那么称a,b是强连通的。

对于有向图上的一个子图,若子图内任意点对(a,b)都满足强连通,则称该子图为强连通子图。

非强连通图有向图的极大强连通子图,称为强连通分量。

对于一个无向图的子图,当删除其中任意一条边后,不改变图内点的连通性,这样的子图叫做边的双连通子图。而当子图的边数达到最大时,叫做边的双连通分量。

将有向图的所有的有向边替换为无向边,所得到的图称为原图的基图。如果一个有向图的基图是连通图,则有向图是弱连通图。

tarjan基本模板

#include<bits/stdc++.h>
using namespace std;
#define N 30000
int dfn
;//深搜的次序
int low
;//能追溯到的最早的次序
int head
;//邻接表
int belong
;//属于哪个强连通分量
int f
;//父节点
bool instack
;
int k,cnt,num;//k是邻接表中边的数量,cnt强连通分量个数,num深搜的次序。
struct edge
{
int u,v,next;
} e[N*5];

void add(int u,int v)
{
e[k].v=v;
e[k].next=head[u];
head[u]=k++;
}

stack<int>s;

void dfs(int u,int id)//有向图不需要参数id
{
dfn[u]=low[u]=++num;
instack[u]=true;
s.push(u);
for(int i=head[u]; i!=-1; i=e[i].next)
{
if(i==(1^id)) continue;//无向图需要这个条件
int v=e[i].v;
if(!dfn[v])
{
f[v]=u;
dfs(v,i);
low[u]=min(low[u],low[v]);
}
else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
cnt++;
int v;
do
{
v=s.top();
s.pop();
instack[v]=false;
belong[v]=cnt;
}
while(u!=v);
}
}

int main()
{
int n,m,x,y;
cin>>n>>m;
memset(head,-1,sizeof(head));
while(m--)
{
cin>>x>>y;
add(x,y);
add(y,x);
}
for(int i=1; i<=n; i++)
{
if(!dfn[i])
{
dfs(i);//有向图
dfs(i,-1);//无向图
}
}
}


相关题目poj1236

题意:给出点的数量,给出与之相连的单向边。至少选几个点,能从这些点到达所有点。至少加多少条边,能从任何一个点到达所有点。

思路:求强连通分量,缩点,求出新图的入度为0的点个数n,出度为0的点个数m。

问题1:ans=n; 问题2:ans=max(n,m);

#include<iostream>
#include<cstring>
#include<stack>
using namespace std;
#define N 155
int n,k,num,cnt;
int head
,dfn
,in
,out
,belong
,low
;
bool instack
;
struct edge
{
int u,v,next;
} e[N*N];
void  add(int u,int v)
{
e[k].u=u;
e[k].v=v;
e[k].next=head[u];
head[u]=k++;
}
stack<int>s;
void dfs(int u)
{
dfn[u]=low[u]=++num;
instack[u]=true;
s.push(u);
for(int i=head[u]; i!=-1; i=e[i].next)
{
int v=e[i].v;
if(!dfn[v])
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
cnt++;
int v;
do
{
v=s.top();
s.pop();
instack[v]=false;
belong[v]=cnt;
}
while(u!=v);
}
}
int main()
{
memset(head,-1,sizeof(head));
cin>>n;
for(int i=1; i<=n; i++)
{
int x;
while(cin>>x,x)
{
add(i,x);
}
}
for(int i=1; i<=n; i++)
{
if(!dfn[i])
dfs(i);
}
for(int i=0; i<k; i++)//求出缩点后的新图中的入度和出度
{
int x=belong[e[i].u],y=belong[e[i].v];
if(x!=y)
in[y]++,out[x]++;
}
int x=0,y=0;
for(int i=1; i<=cnt; i++)
{
if(!in[i]) x++;
if(!out[i]) y++;
}
if(cnt==1)//特例,强连通分量只有一个,不需要加边
{
cout<<1<<endl;
cout<<0<<endl;
}
else
{
cout<<x<<endl;
cout<<max(x,y)<<endl;
}
}


poj2186

题意:有n只牛,牛A认为牛B很牛,牛B认为牛C很牛。给你M个关系(谁认为谁牛),如果牛A认为牛B很牛,牛B认为牛C很牛。那么我们就认为牛A认为牛C很牛。求大家都认为它很牛的牛有几只。

思路:求强连通分量,缩点,出度为0的点只能有一个,答案就是该连通分量中的点的数量。

#include<cstring>
#include<iostream>
#include<stack>
using namespace std;
#define N 30000
int dfn
,low
,head
,belong
,out
;
bool instack
;
int k,cnt,num;
struct edge
{
int u,v,next;
} e[N*5];
void add(int u,int v)
{
e[k].u=u;
e[k].v=v;
e[k].next=head[u];
head[u]=k++;
}
stack<int>s;
void dfs(int u)
{
dfn[u]=low[u]=++num;
instack[u]=true;
s.push(u);
for(int i=head[u]; i!=-1; i=e[i].next)
{
//if(i==(1^id)) continue;
int v=e[i].v;
if(!dfn[v])
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
cnt++;
int v;
do
{
v=s.top();
s.pop();
instack[v]=false;
belong[v]=cnt;
}
while(u!=v);
}
}
int main()
{
int n,m,x,y;
cin>>n>>m;
memset(head,-1,sizeof(head));
while(m--)
{
cin>>x>>y;
add(x,y);
}
for(int i=1; i<=n; i++)
{
if(!dfn[i])
{
dfs(i);
}
}
int c=0;
for(int i=0; i<k; i++)
{
if(belong[e[i].u]!=belong[e[i].v])
out[belong[e[i].u]]++;
}
for(int i=1; i<=cnt; i++)
if(!out[i]) c++;
if(c!=1)
cout<<0<<endl;
else
{
int ans=0;
for(int i=1; i<=n; i++)
if(out[belong[i]]==0)
ans++;
cout<<ans<<endl;
}
}


poj2762

题意:给出一个有向图,求出该图是否能满足给出两个点,能从一个a到b或者从b到a。

思路:一个强连通分量中的点肯定满足,所以先缩点,在该弱连通图中判断改图是否是单链的(toposort),若有分叉则分叉上的两个点是不能互达的。

#include<cstring>
#include<iostream>
#include<stack>
#include<queue>
using namespace std;
#define N 3000
int dfn
,low
,head
,belong
,in
,out
;
bool instack
;
int k,cnt,num;
struct edge
{
int u,v,next;
} e[N*5];
void add(int u,int v)
{
e[k].u=u;
e[k].v=v;
e[k].next=head[u];
head[u]=k++;
}
stack<int>s;
void dfs(int u)
{
dfn[u]=low[u]=++num;
instack[u]=true;
s.push(u);
for(int i=head[u]; i!=-1; i=e[i].next)
{
//if(i==(1^id)) continue;
int v=e[i].v;
if(!dfn[v])
{
dfs(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
cnt++;
int v;
do
{
v=s.top();
s.pop();
instack[v]=false;
belong[v]=cnt;
}
while(u!=v);
}
}
vector<int>E[N*5];//新图的邻接表
queue<int>q;
bool toposort()
{
while(!q.empty()) q.pop();
for(int i=1; i<=cnt; i++)
if(!in[i]) q.push(i);
if(q.size()>1) return false;//入点有多个不满足
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=0; i<E[u].size(); i++)
{
int v=E[u][i];
if(!--in[v])
q.push(v);
}
if(q.size()>1) return false;//有分叉不满足
}
return true;
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n,m,x,y;
cin>>n>>m;
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
num=cnt=k=0;
while(m--)
{
cin>>x>>y;
add(x,y);
}
for(int i=1; i<=n; i++)
{
if(!dfn[i])
{
dfs(i);
}
}
memset(in,0,sizeof(in));
for(int i=1; i<=cnt; i++)
E[i].clear();
for(int i=0; i<k; i++)
{
int x=belong[e[i].u];
int y=belong[e[i].v];
if(x!=y)
{
in[y]++;
E[x].push_back(y);
}
}
if(toposort())
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
}


hdu4738

题意: 曹操有N个岛,这些岛用M座桥连接起来,每座桥有士兵把守(也可能没有),周瑜想让这N个岛不连通,但只能炸掉一座桥,并且炸掉一座桥需要派出不小于守桥士兵数的人。

思路:首先判断图是否连通,不连通则不需要去炸桥,输出0,图连通,则可以用Tarjan找割边,割边不存在输出-1表示不能达到目的,找到所有的割边,只需要炸掉其中守兵数最少的桥即可。

PS: 桥的守兵数为0时,也需要派出一个人去炸桥!

#include<bits/stdc++.h>
using namespace std;
#define N 2000
#define inf 0x7ffffff
int dfn
,low
,head
,belong
,f
;
bool instack
;
int k,cnt,num;
struct edge
{
int u,v,w,next;
} e[N*N];
void add(int u,int v,int w)
{
e[k].u=u;
e[k].v=v;
e[k].w=w;
e[k].next=head[u];
head[u]=k++;
}
stack<int>s;
void dfs(int u,int id)
{
dfn[u]=low[u]=++num;
instack[u]=true;
s.push(u);
for(int i=head[u]; i!=-1; i=e[i].next)
{
if(i==(1^id)) continue;
int v=e[i].v;
if(!dfn[v])
{
f[v]=u;
dfs(v,i);
low[u]=min(low[u],low[v]);
}
else if(instack[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
cnt++;
int v;
do
{
v=s.top();
s.pop();
instack[v]=false;
belong[v]=cnt;
}
while(u!=v);
}
}
int main()
{
int n,m,x,y,w;
while(cin>>n>>m,n+m)
{
memset(head,-1,sizeof(head));
memset(instack,false,sizeof(instack));
memset(f,-1,sizeof(f));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
k=cnt=num=0;
while(m--)
{
cin>>x>>y>>w;
add(x,y,w);
add(y,x,w);
}
int c=0;
for(int i=1; i<=n; i++)
{
if(!dfn[i])
{
c++;
dfs(i,-1);
}
}
int ans=inf;
for(int i=0; i<k; i+=2)
{
int u=e[i].u;
int v=e[i].v;
int w=e[i].w;
if(belong[u]!=belong[v])//满足此条件的是割边
ans=min(ans,w);
}
if(c>1) ans=0;//不连通
else if(ans==0) ans=1;//没有人把守也要派一个人去才能炸掉桥
else if(ans>=inf) ans=-1;//不存在割边
cout<<ans<<endl;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: