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*/


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值