/**
題目大意:
給你一個無向連通圖,問加上一條邊後得到的圖的最少的割邊數;
算法思想:
圖的邊雙連通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;
}
HDU4612(Warm up)2013多校2-圖的邊雙連通問題(Tarjan算法+樹形DP)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.