HDU4612(Warm up)2013多校2-圖的邊雙連通問題(Tarjan算法+樹形DP)

/**
題目大意:
給你一個無向連通圖,問加上一條邊後得到的圖的最少的割邊數;

算法思想:
圖的邊雙連通Tarjan算法+樹形DP;
即通過Tarjan算法對邊雙連通縮圖,構成一棵樹,然後用樹形DP求最長鏈,連接首尾即可;剩下的連通塊即爲所求答案;

算法思路:
對圖深度優先搜索,定義DFN(u)爲u在搜索樹中被遍歷到的次序號;
定義Low(u)爲u或u的子樹中能通過非父子邊追溯到的最早的節點,即DFN序號最小的節點;
則有:
Low(u)=Min
{
    DFN(u),
    Low(v),(u,v)爲樹枝邊,u爲v的父節點
    DFN(v),(u,v)爲指向棧中節點的後向邊(非橫叉邊)
}

一個頂點u是割點,當且僅當滿足(1)或(2)
(1)u爲樹根,且u有多於一個子樹;
(2)u不爲樹根,且滿足存在(u,v)爲樹枝邊(或稱父子邊,即u爲v在搜索樹中的父親),使得DFN(u)<=Low(v);

一條無向邊(u,v)是橋,當且僅當(u,v)爲樹枝邊且滿足DFN(u)<Low(v);

**/
#pragma comment(linker,"/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<stack>
using namespace std;

const int N=200010;
const int M=1000010;

struct node
{
    int v;
    int next;
} e[M*2];

int head[N];
int dfn[N],low[N],dp[N][2];//dp[i][0]表示第i個節點的子樹中最長的鏈,dp[i][1]表示第i個節點的子樹中第二長的鏈
bool visit[M];
int n,m,cnt,res;

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

void Tarjan(int u)
{
    dfn[u]=low[u]=cnt++;
    dp[u][0]=dp[u][1]=0;
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        int j=e[i].v;
        if(!visit[i>>1])
        {
            visit[i>>1]=true;
            if(dfn[j]==0)//跟強連通一樣
            {
                Tarjan(j);
                res+=dfn[u]<low[j];//統計連通塊,比實際數目少一個,就是回溯的時候的最後一個
                int temp=dp[j][0]+(dfn[u]<low[j]);
                if(temp>dp[u][0])
                {
                    dp[u][1]=dp[u][0];
                    dp[u][0]=temp;
                }
                else if(temp>dp[u][1])
                {
                    dp[u][1]=temp;
                }
                low[u]=min(low[u],low[j]);
            }
            else
                low[u]=min(low[u],dfn[j]);
        }
    }
}

int main()
{
	#ifndef ONLINE_JUDGE
    freopen("C:\\Users\\Administrator\\Desktop\\kd.txt","r",stdin);
    #endif
    while(scanf("%d%d",&n,&m)&&n+m)
    {
        cnt=0;
        res=0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(head,-1,sizeof(head));
        for(int i=0; i<m; i++)
        {
        	int u,v;
            scanf("%d%d",&u,&v);
            AddEdge(u,v);
            AddEdge(v,u);
        }
        cnt=1;
        memset(visit,0,sizeof(visit));
        Tarjan(1);
        int temp=0;
        for(int i=1; i<=n; i++)
        {
            temp=max(temp,dp[i][0]+dp[i][1]);//+的時候沒有算當前點所在的塊,但是res也少算一個
        }
        printf("%d\n",res-temp);
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章