hdu 4612 Warm up 多校总结

题意:

求加入一条边(双向)后,图中桥最小的数量。

知识点:

tarjan算法实现无向图缩点(变成树),求树的最大直径

答案 = 连通数 - 树的最大直径 - 1

PS:

输入存在重边,误删;当两个点由两条边相连时,这两个点连通(因为把重边删了,比赛的时候卡死在那里了)
tarjan 算法标记‘边’(无向图)

求树的最大直径,使用一个定理,需要两次求最短路

#pragma comment(linker, "/STACK:10240000000000,10240000000000")	// hdu 人工扩栈
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 202000;
const int MAXN = 2100000;
const int inf = 1000000000;
int min( int a, int b ){ return a < b ? a : b; }
vector<int>dedge[maxn];
struct Edge{
	int v, next, sign;
}edge[MAXN];
int tot, head[maxn];
int n, m;
void init()
{
	int i;
	for( i = 0; i <= n; i++ )dedge[i].clear();
	tot = 0;
	memset( head, -1, sizeof(head) );
}
void add_edge( int u, int v )
{
	edge[tot].v = v;
	edge[tot].sign = 0;
	edge[tot].next = head[u];
	head[u] = tot++;
	edge[tot].v = u;
	edge[tot].sign = 0;
	edge[tot].next = head[v];
	head[v] = tot++;
}
int tmpdfn, dfn[maxn], low[maxn], inst[maxn], belong[maxn], st[maxn], top, scnt;
//	tarjan算法(无向图)
void tarjan( int u )
{
	int v, i, t;
	low[u] = dfn[u] = tmpdfn++;
	st[top++] = u;
	inst[u] = 1;
	for(i = head[u]; i != -1; i = edge[i].next)if( !edge[i].sign )
	{
		v = edge[i].v;
		edge[i].sign = edge[i^1].sign = 1;
		if( dfn[v] == -1 )
		{
			tarjan( v );
			low[u] = min( low[u], low[v] );
			if( low[v] > dfn[u] );//桥
		}
		else if( inst[v] )low[u] = min( low[u], dfn[v] );
	}
	if( low[u] == dfn[u] )
	{
		do{ belong[t = st[--top]] = scnt; inst[t] = 0; }while( t != u );
		scnt++;
	}
}
// 无向图缩点

void SCC()

{
	int i;
	top = 0;
	tmpdfn = scnt = 1;
	memset( dfn, -1, sizeof(dfn) );
	memset( inst, 0, sizeof(inst) );
	for(i = 1; i <= n; i++)if( dfn[i] == -1 )tarjan( i );
}
//	建立所点以后的图(树)
void rebuild()	
{
	int u, v, i;
	for(u = 1; u <= n; u++)
	{
		for(i = head[u]; i != -1; i = edge[i].next)
		{
			v = edge[i].v;
			if( belong[u] != belong[v] )
				dedge[belong[u]].push_back( belong[v] );	
		}
	}
}
// spfa深搜的写法
int dist[maxn];
void spfa_dfs( int u )
{	
	int i, v, size;
	size = dedge[u].size();
	for(i = 0; i < size; i++)
	{
		v = dedge[u][i];
		if( dist[v] > dist[u] + 1 )
		{
			dist[v] = dist[u] + 1;
			spfa_dfs( v );
		}
	}
}
// 求树的最大直径,两次spfa
int treelength()
{
	int i, id;
	fill( dist, dist+n+1, inf );
	dist[1] = 0;
	spfa_dfs( 1 );
	for(id = 1, i = 2; i <= scnt - 1; i++)if( dist[i] > dist[id] )
		id = i; 
	fill( dist, dist+n+1, inf );
	dist[id] = 0;
	spfa_dfs( id );
	for(id = 1, i = 2; i <= scnt - 1; i++)if( dist[i] > dist[id] )
		id = i;	
	return dist[id];
}

int main()
{
	int u, v, i, j, maxlen;
	while( ~scanf( "%d%d", &n, &m ), n + m )
	{
		init();
		for( i = 0; i < m; i++ )
		{
			scanf( "%d%d", &u, &v );
			add_edge( u, v );
		}
		SCC();
		rebuild();
		maxlen = treelength();
		printf( "%d\n", (scnt - 1) - (maxlen + 1) );
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值