图的绝对中心(bzoj 2180: 最小直径生成树)

2180: 最小直径生成树

Time Limit: 10 Sec   Memory Limit: 259 MB
Submit: 219   Solved: 105
[ Submit][ Status][ Discuss]

Description

输入一个无向图G=(V,E),W(a,b)表示边(a,b)之间的长度,求一棵生成树T,使得T的直径最小。树的直径即树的最长链,即树上距离最远的两点之间路径长度。

Input

输入第一行包括两个整数N,M,分别表示点与边的个数。 以下M行,每行3个整数X,Y,Z,描述一条无向边(X,Y),且W(X,Y)=Z。

Output

仅一个数,即最小直径。

Sample Input

3 3
1 2 0
2 3 1
3 1 2

Sample Output

1


图的绝对中心:可以存在于任何一条边上,该点到所有点的最短距离的最大值最小

那么最小直径生成树的直径一定是这个最大值*2

网上貌似没有详细讲最小直径生成树证明的,以前有一篇不过404了,只能暂时存下模板

road[i][j]:i点到j点的最短路

a[i][j]:i点到j点边的长度(当然没有边就INF)

rk[i][j]:i点到其它所有点最短路中第k小的是点i到点rk[i][j]

大致步骤:

①初始化road[i][i]=0,floyd求出任意两点的最短路

②每个点暴力排序它到其他所有点的最短路,求出所有的rk[i][j]

③图的绝对中心可能在点上:初始化ans = min(每个点到相对它最远的那个点的距离*2)

④图的绝对中心可能在边上:暴力每条边E(u, v),从离u点最远的点开始验证


#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define INF 1044266558
int road[305][305], a[305][305], rk[305][305];
int main(void)
{
	int n, m, i, j, k, x, y, ans, now;
	scanf("%d%d", &n, &m);
	memset(road, 62, sizeof(road));
	memset(a, 62, sizeof(a));
	for(i=1;i<=n;i++)
		road[i][i] = 0;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d", &x, &y, &k);
		if(road[x][y]<k)
			continue;
		a[x][y] = a[y][x] = k;
		road[x][y] = road[y][x] = k;
	}
	for(k=1;k<=n;k++)
	{
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=n;j++)
				road[i][j] = min(road[i][j], road[i][k]+road[k][j]);
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
			rk[i][j] = j;
		for(j=1;j<=n;j++)
		{
			for(k=j+1;k<=n;k++)
			{
				if(road[i][rk[i][k]]<road[i][rk[i][j]])
					swap(rk[i][k], rk[i][j]);
			}
		}
	}
	ans = INF;
	for(i=1;i<=n;i++)
		ans = min(ans, road[i][rk[i][j]]);
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			if(road[i][j]==0 || a[i][j]==INF)
				continue;
			now = n;
			for(k=n-1;k>=2;k--)
			{
				if(road[i][rk[j][k]]>road[i][rk[j][now]])
				{
					ans = min(ans, road[j][rk[j][k]]+road[i][rk[j][now]]+a[i][j]);
					now = k;			//now:保证road[i][rk[j][now]]是后缀最大的
				}
			}
		}
	}
	printf("%d\n", ans);
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值