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

来源:互联网 发布:linux安装telnet服务 编辑:程序博客网 时间:2024/05/21 06:11

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 1044266558int 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;}


阅读全文
0 0