hdu 4005 邊雙聯通+LCA
題意:
有一幅圖,現在要加一條邊,加邊之後要你刪除一條邊,使圖不連通,費用為邊的費用,要你求的是刪除的邊的最小值的最大值(每次都可以刪除一條邊,選最小的刪除,這些最小中的最大就為答案)
首先要進行縮點,把圖縮為一棵樹,因此,加入一條邊後圖就會存在一個環,環中的任何一條邊刪除後都不會導致圖不連通
之後找一條最小的邊,可以說這條邊肯定是在加邊之後的連通塊裡的,因為如果不在連通塊裡,那就直接可以把這條最小的邊刪掉,而達不到求出答案的目的
找到邊後,分別從邊的兩點開始遍歷,要遍歷出一條路徑來,並且邊上的權值要儘可能的小,因為這樣才能讓不在環中的邊儘可能的大,然後,答案就是每個節點的次小兒子的最小值,如果沒有次小兒子就不能算(就是說只有一個兒子,即節點不是三叉的),因為我完全可以把它和最小的邊放到一個連通塊中,那樣答案就應該更大了。
終上所述:先進行無向圖的縮點,再在樹上找最小的邊,最後分別從邊的兩點出發,遍歷樹,找節點的次小兒子節點中的最小值
舉個簡單的例子(括號內的數字代表邊上的權值)1和8間的權值為1,是最小的
1---8
/ \(3)
(2)/ \
2 3
(4) / \(5) (6)/ \(7)
/ \ / \
4 5 6 7
左子樹中2的子節點有次小值5,右子樹中3的子節點次小值為7,兩個次小值間的最小值是5,即答案
現在,比如所你要把3、4連起來。我可以去掉2、5之間的邊讓圖不連通,花費為5
把3、5連起來,我自然可以刪掉2、4,花費為4,
一個節點的次小值和最小值(比如說4、5兩點)不可能被同時連進一個連通塊(或環)中(因為必須把最小的那條邊加進環中),正是利用這個性質,不管把那兩個點連起來,我們都可以找到一個最小值或次小值來刪掉使圖不連通,注意:再重複一遍,同一個節點的最小值和次小值不會被加進同一個環,因此,這些次小值中的最小的那條邊的權值就是答案。(這時你如果把次小的邊加進環中,如2--5,自然可以刪掉一條更小的邊 如2--4 使圖不連通,相反,如果沒有把次小的邊加進去,那次小的就是答案)
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn = 10010;
int tot,n,m,ans;
const int inf = 999999999;
struct Edge
{
int t,w;
int next;
int vis;
}edge[1000005];
int head[maxn],E;
void add(int s,int t,int w)
{
edge[E].t=t;
edge[E].w=w;
edge[E].vis=0;
edge[E].next=head[s];
head[s]=E++;
}
int Btype,Time,N,M;
int dfn[maxn],low[maxn],belong[maxn];
int st[maxn],Top;
int tt[100010][3],cnt;
inline int min(int a,int b){return a<b?a:b;}
void dfs(int s)
{
int i,t;
st[++Top]=s;
dfn[s]=low[s]=++Time;
for (i=head[s];i!=-1;i=edge[i].next)
{
if(edge[i].vis)continue;
edge[i].vis=edge[i^1].vis=1;
t=edge[i].t;
if (!dfn[t])
{
dfs(t);
low[s]=min(low[s],low[t]);
if(dfn[s]<low[t])
{
//printf("bug");
tt[++cnt][0]=s,tt[cnt][1]=t,tt[cnt][2]=edge[i].w;
}
}
else low[s]=min(low[s],dfn[t]);
}
if(dfn[s]==low[s])
{
Btype++;
do{
t=st[Top--];
belong[t]=Btype;
}while(t!=s);
}
}
void SCC(int n)
{
int i;
Time=0;Btype=0;Top=0;
memset(dfn,0,sizeof(int)*(n+1));
for(i=1;i<=n;i++)if(!dfn[i])
dfs(i);
}
int find(int s,int t)
{
int i;
int Min=inf,vice_Min=inf,rr=inf;
for(i=head[s];i!=-1;i=edge[i].next)
{
int v=edge[i].t;
if(v==t) continue;
int w=find(v,s);//printf("w=%d\n",w);
if(w<vice_Min) vice_Min=w;
if(edge[i].w<vice_Min) vice_Min=edge[i].w;
if(Min>vice_Min) swap(vice_Min,Min);
if(Min<rr) rr=Min;
}
if(ans>vice_Min) ans=vice_Min;
return rr;
}
int a1,a2,flag;
int main()
{
int i,a,b,w;
while(scanf("%d%d",&n,&m)!=EOF)
{
memset(head,-1,sizeof(head));E=0;
for(i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&w);
add(a,b,w);
add(b,a,w);
}
cnt=0;
SCC(n);
memset(head,-1,sizeof(head));E=0;
int C=inf;
for(i=1;i<=cnt;i++)
{
// printf("s=%d t=%d\n",tt[i][0],tt[i][1]);
add(belong[tt[i][0]],belong[tt[i][1]],tt[i][2]);
add(belong[tt[i][1]],belong[tt[i][0]],tt[i][2]);
if(tt[i][2]<C){C=tt[i][2]; a=belong[tt[i][0]],b=belong[tt[i][1]];}
}
ans=inf;
find(a,b);
find(b,a);
if(ans==inf) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}
/*
7 6
1 2 2
1 3 6
2 4 3
2 5 4
3 6 5
3 7 7
4
15 14
1 2 7
2 4 5
2 5 6
4 8 1
4 9 2
5 10 3
5 11 4
1 3 8
3 6 9
3 7 1
6 12 11
6 13 12
7 14 13
7 15 14
2
16 15
1 2 7
2 4 5
2 5 6
4 8 1
4 9 2
5 10 3
5 11 4
1 16 1
16 3 6
3 6 9
3 7 1
6 12 11
6 13 12
7 14 13
7 15 14
2
6 5
1 2 1
1 3 2
1 4 3
2 5 4
2 6 5
3
9 8
1 2 1
2 6 5
6 7 7
6 8 4
6 9 6
1 3 2
1 4 5
1 5 6
5
9 8
1 2 1
2 6 5
6 7 7
6 8 4
6 9 4
1 3 2
1 4 5
1 5 6
4*/