HDU4612-强连通分量&树的直径-warmup

给定一个图,要求你添加一个边,使这个图的桥最少。
我几乎是蒙蔽的。
先缩个点,然后求一遍树的直径。
然后用 原先的桥数 减去树的直径就好了。
或者 用强连通分量树 -1-树的直径
(缩点后一个scc就是一个点,而桥就是边,并且是树,如果不是树就缩点缩了)
这里写图片描述
发现了吧。有的人把剩下的桥 叫做 枝叶边, 就是那个红点那个边
就是要求的 剩下的最小的桥数
(要求添加的那个一边,从直径头到尾,把所有直径边想当桥的愿望都给否定了)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <bits/stdc++.h>
/*先缩点,变成一个树,然后再求树的直径,然后那个
原来的桥数减去直径  在减1(因为还要在添)
*/
using namespace std;
const int maxm=1000008;
const int maxn=200005;
struct Node
{  int to,next,cost;
}node[maxm*2];
int len;
int head[maxn];
void add(int u,int v){
   node[len].to=v;
   node[len].next=head[u];
   //node[len].cost=w;
   head[u]=len++;
}
int low[maxn];
int dfn[maxn];
int cnt[maxn];
int num[maxn];
int index;
int bridge;
int scc;
vector<int>G[maxn];
stack<int>s;
int m,n,a,b;
void init(){
  index=0;
  len=0;
  scc=0;
  bridge=0;
  memset(head,-1,sizeof(head));//链表
  memset(num,0,sizeof(num));//各scc多少个
  memset(dfn,0,sizeof(dfn));//
  memset(low,0,sizeof(low));
  for(int i=0;i<maxn;i++)
     G[i].clear();
    while(!s.empty())
         s.pop();
}
void tarjan(int m,int pre){
     low[m]=dfn[m]=++index;
     s.push(m);
     for(int i=head[m];i!=-1;i=node[i].next)
    {
        if(i==(pre^1))continue;
        int v=node[i].to;
        if(!dfn[v])
            {
            tarjan(v,i);
            if(low[v]<low[m])
                low[m]=low[v];
            if(low[v]>dfn[m])//这就是 桥的条件
                bridge++;
        }else if(dfn[v]<low[m])//下一个点的dfs顺序比他能到达的小,就换
            low[m]=dfn[v];
    }
     if(low[m]==dfn[m])
    {
        scc++;
        for(;;)
        {
            int x = s.top();s.pop();
            cnt[x]=scc;
            num[scc]++;
            if(x==m)break;
        }
    }
}
int dis[maxn];
bool vis[maxn];
int dfs(int a){
    for(int i=0;i<G[a].size();i++){
        int v=G[a][i];
        if(!vis[v])
        {vis[v]=true;
        dis[v]=dis[a]+1;
        dfs(v);
        }
    }
}
void solve(){
   memset(dis,0,sizeof(dis));
   memset(vis,false,sizeof(vis));
   vis[1]=true;
   dis[1]=0;
   dfs(1);
   int dex=1;
   int sum=-1;
   for(int i=1;i<=scc;i++){
      if(dis[i]>sum)
        {sum=dis[i];//
         dex=i;
        }
   }
   memset(dis,0,sizeof(dis));
   memset(vis,false,sizeof(vis));
   vis[dex]=true;
   dis[dex]=0;
   dfs(dex);
   sum=-1;
   dex=1;
   for(int i=1;i<=scc;i++){
       if(dis[i]>sum)
         sum=dis[i];
   }
  // cout<<bridge<<endl;
   //cout<<sum<<endl;
//printf("%d\n",bridge-sum);
printf("%d\n",scc-1-sum);
}
int main()
{   while(~scanf("%d%d",&m,&n)){
        if(m==0||n==0) break;
         init();
         for(int i=1;i<=n;i++){
            scanf("%d%d",&a,&b);
            add(a,b);
            add(b,a);
         }
         for(int i=1;i<=m;i++)
             if(!dfn[i])
            tarjan(i,-1);
         for(int i=1;i<=m;i++){
            for(int j=head[i];j!=-1;j=node[j].next)
            {  int v=node[j].to;
                if(cnt[v]==cnt[i]) continue;
                G[cnt[v]].push_back(cnt[i]);
                G[cnt[i]].push_back(cnt[v]);//缩点
            }
         }
         solve();



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值