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

题意:给出一个无向图,可能包含重边和自环,现在可以在这个图上加上一条边,问加完这条边后图上最少有多少桥边。

思路:首先求出所有边双连通分量,然后缩点,缩点以后形成了一棵树,那么只要我们将树上最长的一条链变成一个环,那么肯定可以减少最多的桥边,问题就变成了求出边双连通分量,这道题一开始一直wa,因为没考虑清楚重边的影响,将tarjan里的判断当前点是否是父亲结点改成判断当前边是否是从父亲结点连过来的边,那么我们只需要对所有边编号即可。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int MAXN = 200100;//点数
const int MAXM = 2000100;//边数
int n, m;
struct Edge
{
    int from, to, next, id;
    bool cut;//是否是桥标记
}edge[MAXM];
int head[MAXN], tot;
int Low[MAXN],DFN[MAXN],Stack[MAXN],Belong[MAXN];//Belong数组的值是1~Block
int Index, Top;
int Block;//边双连通块数
bool Instack[MAXN];
vector<int> G[MAXN]; //所点后形成的图 
void addedge(int u,int v,int i)
{
    edge[tot].from = u;edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut=false;edge[tot].id = i;
    head[u] = tot++;
}
void init()
{
	memset(head,-1,sizeof(head));
	memset(DFN,0,sizeof(DFN));
    memset(Instack,false,sizeof(Instack));
    Index = Top = Block = tot = 0;
    for(int i = 1; i <= n; i++) G[i].clear();
}
void Tarjan(int u,int id)
{
    int v;
    Low[u] = DFN[u] = ++Index;
    Stack[Top++] = u;
    Instack[u] = true;
    for(int i = head[u];i != -1;i = edge[i].next)
    {
        v = edge[i].to;
        if(edge[i].id == id) continue;
        if( !DFN[v] )
        {
            Tarjan(v, edge[i].id);
            if( Low[u] > Low[v] )Low[u] = Low[v];
        }
        else if( Instack[v] && Low[u] > DFN[v] )
            Low[u] = DFN[v];
    }
    if(Low[u] == DFN[u])
    {
        Block++;
        do
        {
            v = Stack[--Top];
            Instack[v] = false;
            Belong[v] = Block;
        }
        while( v!=u );
    }
}
void suodian() {
	for(int i = 1; i <= n; i++) {
		for(int e = head[i]; e != -1; e = edge[e].next) {
			int u = edge[e].to;
			if(Belong[u] != Belong[i]) G[Belong[i]].push_back(Belong[u]);
		}
	}
}
bool vis[MAXN];
int dist[MAXN];
int bfs(int cur) {
	memset(vis, 0, sizeof(vis));
	queue<int> q;
	q.push(cur);
	vis[cur] = 1;
	dist[cur] = 0;
	int t;
	while(!q.empty()) {
		t = q.front(); q.pop();
		for(int i = 0; i < G[t].size(); i++) {
			int u = G[t][i];
			if(vis[u]) continue;
			dist[u] = dist[t] + 1;
			vis[u] = 1;
			q.push(u);
		}
	}
	return t;
}

int main() {
    //freopen("input.txt", "r", stdin);
	while(scanf("%d%d", &n, &m)==2 && n) {
		init();
		for(int i = 1; i <= m; i++) {
			int u, v; scanf("%d%d", &u, &v);
			if(u == v) continue;
			addedge(u, v, i);
			addedge(v, u, i);
		}
		Tarjan(1, -1);
		suodian();
		int maxlen = dist[bfs(bfs(1))];
		cout << Block-1-maxlen << endl;
	}
    return 0;
}


















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值