Sweety

Practice makes perfect

导航

连通图模板

Posted on 2016-05-09 14:29  蓝空  阅读(189)  评论(0编辑  收藏  举报

强连通分量

模板:(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--;
}

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

//#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;
}




构造双连通图
模板: