HDU 4612 Warm up (边双连通分量+缩点+树的直径)

<题目链接>

题目大意:
给出一个连通图,问你在这个连通图上加一条边,使该连通图的桥的数量最小,输出最少的桥的数量。

解题分析:

首先,通过Tarjan缩点,将该图缩成一颗树,树上的每个节点都是一个边双连通分量,树上的每条边都是桥,现在需要挑出两个点,将它们直接相连,这样它们原始路径上所有的桥因为形成了环而全部消失,因此为了使剩下的桥最少,我们需要找到路径上桥最多的两点,又由于缩点后,树的每条边都是桥,所以这里就转化为树上距离两点的最远距离,也就是求树的直径。

下面Tarjan的时候需要注意的是,vis记录的是访问过边的编号不是节点的编号。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <queue>
  6 using namespace std;
  7 #define mem(a,b) memset(a,b,sizeof(a))
  8 typedef pair<int, int> pll;
  9 const int N = 2e5+5;
 10 const int M = 1e6+5;
 11 int num, num1, top, cnum,maxdis, pos,cnt;
 12 int head[N], head1[N], dis[N],instk[N], stk[N], dfn[N], low[N], belong[N];
 13 int  vis[M<<1];
 14 struct Edge{
 15     int to, next;
 16 }e[M<<1], e1[M<<1];
 17 
 18 void add(int u, int v) {
 19     e[num].to = v, e[num].next = head[u], head[u] = num++;
 20 }
 21 void add1(int u, int v) {
 22     e1[num1].to = v, e1[num1].next = head1[u], head1[u] = num1++;
 23 }
 24 void init() {
 25     num = num1 = cnt = cnum = top = 0;
 26     mem(head,-1),mem(head1,-1),mem(belong,0),mem(instk,0);
 27     mem(vis,0),mem(stk,0),mem(dfn,0),mem(low,0),mem(dis,0);
 28 }
 29 pll edge[M];
 30 void tarjan(int u){
 31     dfn[u] = low[u] = ++cnt;
 32     instk[u] = 1;
 33     stk[++top] = u;
 34     for(int i = head[u]; ~i; i = e[i].next) {
 35         int v = e[i].to;
 36         if(vis[i])continue;
 37         vis[i] = vis[i^1] = 1;    //将正、反两边全部标记
 38         if(!dfn[v]) {
 39             tarjan(v);
 40             low[u] = min(low[u], low[v]);
 41         } else if(instk[v]) {
 42             low[u] = min(low[u], dfn[v]);
 43         }
 44     }
 45     if(low[u] == dfn[u]) {
 46         ++cnum;      //cnum为边双连通分量的数量
 47         int v;
 48         do{
 49             v = stk[top--];
 50             instk[v] = 0;
 51             belong[v] = cnum;     //将该连通块染色
 52         } while(v != u);
 53     }
 54 }
 55 void bfs(int u){
 56     queue <int> q;
 57     q.push(u);
 58     dis[u] = 0;
 59     mem(vis,0);
 60     vis[u] = 1;
 61     maxdis = 0, pos = u;
 62     while(!q.empty()) {
 63         int u = q.front(); q.pop();
 64         for(int i = head1[u]; ~i; i = e1[i].next) {
 65             int v = e1[i].to;
 66             if(vis[v])continue;
 67             vis[v] = 1;
 68             dis[v] = dis[u]+1;
 69             if(dis[v]>maxdis) {     //更新树上的最远距离
 70                 maxdis = dis[v];
 71                 pos = v;     //并且记录下该点
 72             }
 73             q.push(v);
 74         }
 75     }
 76 }
 77 int main()
 78 {
 79     int n, m, x, y;
 80     while(scanf("%d%d",&n,&m)!=EOF,n||m){
 81         init();
 82         for(int i = 0; i<m; i++) {
 83             scanf("%d%d", &x, &y);
 84             edge[i].first = x, edge[i].second = y;  //记录下所有的边,用结构体记录也可以
 85             add(x, y),add(y, x);
 86         }
 87         tarjan(1);
 88         int edgenum = 0;
 89         for(int i = 0; i<m; i++){    //将缩点后的所有点建图,跑BFS,求树的直径
 90             int x = edge[i].first, y = edge[i].second; 
 91             if(belong[x]!=belong[y]) {
 92                 add1(belong[x], belong[y]);
 93                 add1(belong[y], belong[x]);
 94                 edgenum++;     //桥的数量+1
 95             }
 96         }
 97         bfs(belong[1]);
 98         bfs(pos);     //求出此时树的直径,即一条路径上所含的最多桥的数量
 99         int ans = edgenum-maxdis;
100         printf("%d\n", ans);
101     }
102 }

 

 

2018-11-07

转载于:https://www.cnblogs.com/00isok/p/9926367.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值