#include<bits/stdc++.h>
const int maxn=205,maxm=50010;
using namespace std; int ans=(int)1e9;
int n,m,d[maxn][maxn],inf,tot,rank[maxn][maxn];
struct Edge{int x,y,v;}E[maxm];
void floyd(){ //强行求出任意两点间最短距离
for (int k=1;k<=n;k++)
for (int i=1;i<=n;i++) if (i!=k)
for (int j=1;j<=n;j++) if (i!=j&&k!=j)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
void work(int id){
int u=E[id].x,v=E[id].y,L=E[id].v;
for (int a=1,b=2;b<=n;b++){
if (d[v][rank[u][a]]>d[v][rank[u][b]]) continue;
ans=min(ans,d[v][rank[u][a]]+d[u][rank[u][b]]+L);
a=b;
}
}
int main(){
scanf("%d%d",&n,&m);
memset(d,63,sizeof(d)),inf=d[0][0];
for (int i=1;i<=n;i++) d[i][i]=0;
for (int i=1,x,y,z;i<=m;i++) {
scanf("%d%d%d",&x,&y,&z);
d[x][y]=d[y][x]=min(d[x][y],z);
}
for (int i=1;i<=n;i++)
for (int j=1;j<i;j++)
if(d[i][j]!=inf)
E[++tot]=(Edge){j,i,d[i][j]};
floyd();
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
rank[i][j]=j;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=j+1;k<=n;k++)
if (d[i][rank[i][j]]<d[i][rank[i][k]])
swap(rank[i][j],rank[i][k]);
for (int i=1;i<=n;i++)
ans=min(ans,d[i][rank[i][1]]+d[i][rank[i][2]]);
for (int i=1;i<=tot;i++) work(i);
printf("%d\n",ans);}
最小直径生成树
题目:bzoj2180
图的绝对中心就是这样一个点。它可以存在于任何一条边上,并且这个点到所有点的最短距离的最大值最小。
绝对中心到所有点的最短距离的最大值肯定会有两个,而且这两个最短距离位于某条边的两端,也就是说加入绝对中心在u v这条边上,那么肯定存在两个距离相等且最大的最短路径,一条从绝对中心往u方向走,另一条从绝对中心往v方向走
然后我们枚举每一条边,假设绝对中心在这条边上,中心到某个点的最短距离可以表示为min(d[u][s] + x , d[v][s] + L - x) 。设点S在V端,就取右值,S在U端就取左值(X就是从U往V走的距离)
注意画出函数图像来的话就是一条折线。在这条折线上取一个最小值就是中心到s的最短距离的最小值,那到每个点都是这样一条折线,由于要取最短距离的最大值,所以我们的点得在所有折线构成的最上方的折线上取一个最低点
注意以上折线图是一条边到所有点的情况,现需要求全部边对应自身折线图的最低点里的最大值。
将d[u][w]排序,扫描一遍就能求出所有的交点,问题完美解决。
Input ouput
8 11 13
1 3 1
1 2 2
2 4 3
2 6 7
4 6 0
6 8 4
8 7 3
7 5 1
6 3 5
7 3 5
5 3 6
每边曲线代表一 条边,值是MAX(到U点端最远点距离+X,到V点端最远点距离+L-X)
横坐标是边中长度占比,由U端走到V端的距离,0到W(U,V)