题意:
n个点m条边,无向图,每条边有一个值,如果我们删除这条边,我们需要花费的金额等同这条边上的值。最开始整个图是连通的,现在会加上一条未知的边进去形成新图,我们只能删除一条边,保证新图不连通,求需要的最小金额是多少。
分析:
无向图求连通分量。我们首先应该知道,因为加入的边未知,所以我们所求的最小金额就是无论加入的边在图中哪里,我们都可以删除一条边来使它不连通。so我们首先对图求解连通分量,然后缩点,形成一棵树。这时候,我们可以想到,在加入一条未知的边之后会形成一个环,显然最小值的边不是我们的答案,因为这条边有可能就在这个环中,这时候我们就删除不了边了(其他边的值都比这个值大)。
我们仔细想想可以发现:通过以结点u为根的子树时,一定会经过以结点u为根的子树中边权最小的那条边,那么这时候我们除去这条路径后剩下的边权中的最小值就是我们要求的答案了。所以,我们的算法思想就是:递归求解每个结点为根的子树中的最小边和次小边,我们必须保证这里最小边和次小边不可能在一条路径上,然后求次小边中的最小值就是答案了。
由上图来说,答案是3,
分三种情况来说,
- v左子树跟v右子树建一条边,那么桥就在权值是1的边上。
- v左子树跟非v子节点建立一条边,那么桥就在权值为3的边上。
- v右子树跟非v子节点建立一条边,那么桥就在权值为2的边上。
所以找取所有节点非一条路径上的此小值的最小值即可。
总结:
连通分量的一道很好的题,保证最小边和次小边不能有机会在一条路径上的解决方法上很巧妙。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
const int maxm=2e5+10;
struct EDGE{
int to,next;
bool cut;
int w;
}edge[maxm];
int head[maxn],tot;
int low[maxn],dfn[maxn],sta[maxn],belong[maxn];
int index,top;
int block;
bool instack[maxn];
int bridge;
void addedge(int u,int v,int w){
edge[tot].to=v;
edge[tot].w=w;
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;
sta[top++]=u;
instack[u]=true;
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]){
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=sta[--top];
instack[v]=false;
belong[v]=block;
}while(v!=u);
}
}
void init(){
block=top=tot=index=0;
memset(head,-1,sizeof head);
memset(dfn,0,sizeof dfn);
memset(instack,0,sizeof instack);
}
struct node{
int u,v,w;
node(int u=0,int v=0,int w=0):u(u),v(v),w(w){}
}tmp;
vector<node> g[maxn];
const int inf=INT_MAX;
int ans;
int dfs(int u,int pre){
int fir=inf,sec=inf;
for(int i=0;i<g[u].size();i++){
int v=g[u][i].v;
int w=g[u][i].w;
// printf("-- %d %d %d \n",u,v,w);
if(v==pre) continue;
int t=dfs(v,u);
int mi=min(t,w);
if(mi<=fir){
sec=fir;
fir=mi;
}else{
sec=min(sec,mi);
}
}
ans=min(ans,sec);
return fir;
}
int main(){
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
init();
while(m--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i,0);
}
// printf("%d\n",block);
int mi=inf;
for(int i=1;i<=block;i++)g[i].clear();
for(int u=1;u<=n;u++){
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
int w=edge[i].w;
if(belong[u]!=belong[v]){
g[belong[u]].push_back(node(belong[u],belong[v],w));
// g[belong[v]].push_back(node(belong[v],belong[u],w));
if(mi>w){
tmp.u=belong[u];tmp.v=belong[v];tmp.w=w;
mi=w;
}
}
}
}
// printf("-- %d\n",tmp.w);
// for(int i=0;i<)
ans=inf;
dfs(tmp.u,tmp.v);
dfs(tmp.v,tmp.u);
if(ans==inf) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}