您的位置:首页 > 其它

连通图模板

2016-05-09 14:29 169 查看
强连通分量

模板:(output用来在原来不是强连通的基础上求加多少边变为强连通)

矩阵存储

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXV 110
#define min(a,b) (a>b?b:a)
#define max(a,b) (a>b?a:b)

int n,map[MAXV][MAXV],outdegree[MAXV],indegree[MAXV];
int dfn[MAXV];									//第一次访问的步数
int low[MAXV];									//子树中最早的步数
int stap[MAXV],stop;							//模拟栈
bool instack[MAXV];								//是否在栈中
int count;										//记录连通分量的个数
int cnt;										//记录搜索步数
int belong[MAXV];								//属于哪个连通分量

void init(){
count=stop=cnt=0;
memset(instack,false,sizeof(instack));
memset(map,0,sizeof(map));
memset(dfn,0,sizeof(dfn));
}

void tarjan(int x){
int i;
dfn[x]=low[x]=++cnt;
stap[stop++]=x;
instack[x]=true;
for(i=1;i<=n;i++){
if(!map[x][i]) continue;
if(!dfn[i]){
tarjan(i);
low[x]=min(low[i],low[x]);
}else if(instack[i])
low[x]=min(dfn[i],low[x]);
//与x相连,但是i已经被访问过,且还在栈中
//用子树节点更新节点第一次出现的时间
}

if(low[x]==dfn[x]){
count++;
while(1){
int tmp=stap[--stop];
belong[tmp]=count;
instack[tmp]=false;
if(tmp==x) break;
}
}
}

void output(){
int i,j,inzero=0,outzero=0;
for(i=1;i<=n;i++){
indegree[i]=outdegree[i]=0;
}
for(i=1;i<=n;i++)				//找连通分量入度与出度
for(j=1;j<=n;j++)
if(map[i][j] && belong[i]!=belong[j]){
indegree[belong[j]]++;
outdegree[belong[i]]++;
}
for(i=1;i<=count;i++){			//找入度与出度为0的点
if(!indegree[i]) inzero++;
if(!outdegree[i]) outzero++;
}

if(count==1)					//只有1个结点要特判
printf("1\n0\n");
else
printf("%d\n%d\n",inzero,max(inzero,outzero));
}

int main(){
int i,a;
while(~scanf("%d",&n)){
init();
for(i=1;i<=n;i++){
while(scanf("%d",&a) && a) map[i][a]=1;
}
for(i=1;i<=n;i++)
if(!dfn[i])	tarjan(i);
output();
}
return 0;
}
邻接表存储:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MOD 100000
#define inf 1<<29
#define LL long long
#define MAXN 20010
#define MAXM = 50010
using namespace std;
struct Edge
{
int to,next;
bool cut;
} edge[MAXN];

int head[MAXN],tot;
int low[MAXN],DFN[MAXN],belong[MAXN];///belong 的值为1-block
int index,top,fenzhiNum;
int block ;  ///强连通分量
bool inStack[MAXN];
int bridgeNum;  ///桥的数目
int stack[MAXN];
int vis[MAXN];
int inans,outans;
int outdu[MAXN];
int indu[MAXN];

void addedge(int u,int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
edge[tot].cut = false;
head[u] = tot++ ;
}
void ini(){
index = block = top = fenzhiNum = 0;
inans = 0, outans = 0 ;
memset(DFN,0,sizeof(DFN));
memset(inStack,false,sizeof(inStack));
memset(vis,0,sizeof(vis));
memset(outdu,0,sizeof(outdu));
memset(indu,0,sizeof(indu));
memset(head,-1,sizeof(head));
}
void Tarjan(int u)
{
vis[u] = true;
int v;
low[u] = DFN[u] = ++index;
stack[top++] = u;
inStack[u] = true;
for(int i=head[u] ; i!=-1 ; i=edge[i].next)
{
v = edge[i].to;
//if( v == pre ) continue;    ///因为是无向图,所以两条是双向的,所以只遍历一条就够了
if( !DFN[v] )
{
Tarjan(v );
if(low[u]>low[v])
low[u] = low[v];
if(low[v] > DFN[u] ){
bridgeNum++;
edge[i].cut = true;
//edge[i^1].cut = true;  ///将两条双向边都设置为桥
}

}
else if( inStack[v] && low[u] > DFN[v])
low[u] = DFN[v];
}
if(low[u] == DFN[u])
{
block++;
do
{
v=stack[--top];  ///清空当前强连通分量栈 必须清空
inStack[v] = false;
belong[v]=block;   ///v节点都编号为block  也就是这是一个块
}
while(v!=u);
}
}

void solve(int N)
{
for(int i=1;i<=N;i++)
if(!vis[i])
Tarjan(i);
for(int i=1; i<=N ; i++){  ///缩点
for(int j=head[i] ; j!=-1 ; j=edge[j].next)
if( belong[i]!=belong[ edge[j].to ] )
indu[ belong[ edge[j].to ] ]++,outdu[ belong[i] ]++ ;
}
for(int i=1;i<=block ;i++)
if(indu[i] == 0)
inans++;
for(int i=1;i<=block ;i++)
if(outdu[i] == 0)
outans++;
// printf("indu=%d,outdu=%d\n",inans,outans);
if(block == 1) printf("1\n0\n");
else printf("%d\n%d\n",inans,max(inans,outans));
//printf("%d\n",(ans+1)/2 );
}

int main ()
{
int n,m;
while(~scanf("%d",&n))
{
int u,v,mark=0;
tot=0;
ini();
for(int i=1; i<=n; i++)
{
while(scanf("%d",&u)&&u!=0){
mark=0;
for(int j=head[i] ; j!=-1 ; j=edge[j].next)  ///去重边
if(edge[j].to == u){
mark = 1;
break;
}
if(!mark) addedge(i,u);
}
}
solve(n);
}
return 0;
}


割点与桥

模板:

/*
*  求  无向图   的割点和桥
*  可以找出割点和桥,求删掉每个点后增加的连通块。
*  需要注意重边的处理,可以先用矩阵存,再转邻接表,或者进行判重
*/
const int MAXN = 10010;
const int MAXM = 2000010;
struct Edge
{
int to,next;
bool cut;//是否为桥的标记
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN];
int Index,top;
bool Instack[MAXN];
bool cut[MAXN];   ///记录是否是割点
int add_block[MAXN];//删除一个点(i)后增加的连通块
int bridge;

void addedge(int u,int v)
{
edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut = false;
head[u] = tot++;
}

void Tarjan(int u,int pre)  ///pre是父节点,用来判断重边
{
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
Instack[u] = true;
int son = 0;
int pre_cnt = 0;  ///处理重边 ,如果不需要可以去掉
for(int i = head[u];i != -1;i = edge[i].next)
{
v = edge[i].to;
if(v == pre && pre_cnt == 0)
{
pre_cnt++;
continue;
}
if( !DFN[v] )
{
son++;
Tarjan(v,u);
if(Low[u] > Low[v])
Low[u] = Low[v];
///桥
///一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)。
if(Low[v] > DFN[u])
{
bridge++;
edge[i].cut = true;
edge[i^1].cut = true;
}
//割点
//一个顶点u是割点,当且仅当满足(1)或(2)
//(1) u为树根,且u有多于一个子树。
//(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)
if(u != pre && Low[v] >= DFN[u])//不是树根
{
cut[u] = true;
add_block[u]++;
}
}
else if( Low[u] > DFN[v])
Low[u] = DFN[v];
}
//树根,分支数大于1
if(u == pre && son > 1)
cut[u] = true;
if(u == pre)
add_block[u] = son - 1;
Instack[u] = false;
top--;
}



边双连通分量(也就是求桥的个数,这不过这道题比较经典)

模板:http://blog.csdn.net/u014665013/article/details/51377421

//#pragma comment(linker, "/STACK:102400000,102400000"
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define MAX 100005
#define INF 0x3f3f3f3f
#define LL long long
#define pii pair<int,int>
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
using namespace std;

const int MAXN = 200010;//点数
const int MAXM = 2000010;//边数,因为是无向图,所以这个值要*2
struct Edge
{
int to,next;
bool cut;//是否是桥标记
}edge[MAXM];
int head[MAXN],tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1~block
int Index,top;
int block;//边双连通块数
bool Instack[MAXN];
int bridge;//桥的数目

void addedge(int u,int v)
{
edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut=false;
head[u] = tot++;
}

void Tarjan(int u,int pre)
{
int v;
Low[u] = DFN[u] = ++Index;
Stack[top++] = u;
int pre_cnt=0;  ///处理重边
Instack[u] = true;
for(int i = head[u];i != -1;i = edge[i].next)
{
v = edge[i].to;
if(v == pre && pre_cnt == 0 ){
pre_cnt ++;
continue;
}
if( !DFN[v] )
{
Tarjan(v,u);
if( Low[u] > Low[v] )Low[u] = Low[v];
if(Low[v] > DFN[u])
{
bridge++;
edge[i].cut = true;
edge[i^1].cut = true;
}
}
else if( Instack[v] && Low[u] > DFN[v] )
Low[u] = DFN[v];
}
if(Low[u] == DFN[u])
{
block++;
do
{
v = Stack[--top];
Instack[v] = false;
Belong[v] = block;
}
while( v!=u );
}
}
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
}

int du[MAXN];//缩点后形成树,每个点的度数
vector<int>vec[MAXN];
int dep[MAXN];
void dfs(int u)
{
for(int i = 0;i < vec[u].size();i++)
{
int v = vec[u][i];
if(dep[v]!=-1)continue;
dep[v]=dep[u]+1;
dfs(v);
}
}
void solve(int n)
{
memset(DFN,0,sizeof(DFN));
memset(Instack,false,sizeof(Instack));
Index = top = block = 0;
Tarjan(1,0);
for(int i = 1;i <= block;i++)
vec[i].clear();
for(int i = 1;i <= n;i++)
for(int j = head[i];j != -1;j = edge[j].next)
if(edge[j].cut)
{
vec[Belong[i]].push_back(Belong[edge[j].to]);
}
memset(dep,-1,sizeof(dep));
dep[1]=0;
dfs(1); ///第一次dfs找距离1节点最远的节点k
int k = 1;
for(int i = 1;i <= block;i++)
if(dep[i]>dep[k])
k = i;
memset(dep,-1,sizeof(dep));
dep[k]=0;
dfs(k);  ///第二次dfs找出距离k最远的节点,也就是树的直径
int ans = 0;
for(int i = 1;i <= block;i++)
ans = max(ans,dep[i]);
printf("%d\n",block-1-ans);
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,m;
int u,v;
while(scanf("%d%d",&n,&m)==2)
{
if(n==0 && m==0)break;
init();
for(int i = 0;i < m;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
solve(n);
}
return 0;
}


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