2180: 最小直径生成树
Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 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;
}