图的绝对中心
在图中找到一个点,使得它到所有其他点的最短路径的最大值尽可能小。
首先绝对中心到最远的两个点的距离一定是相等的。我们直接枚举每条边(u,v),考虑这个绝对中心在距离u为x的位置。这时我们考虑这条边以外的任意一个点w,那么可能的答案为min(dis[u][w]+x,dis[v][w]+g[u][v]-x),我们将其画图,x为横坐标,答案为纵坐标,会发现图像是一条折线。
我们多尝试画一些点,会得到一些折线,看图像可以发现最优值只会在折线的最低点取到,而且这些最低点只会出现在交点处。观察会发现dis[u][w]>dis[u][w’]且dis[v][w]<disp[u][w’]时才会产生交点。所以我们排序所有点到任意点的距离,枚举时就保证了第一维,这样只要保证第二维即可。
最后只要求出交点即可,根据到最远的两个点距离相等,我们有等式dis[u][w]+x=dis[v][w’]+g[u][v]-x,这样就可以求出x与最长距离了。
ll g[maxn][maxn],dis[maxn][maxn],rak[maxn][maxn];
//g表示图,dis表示任意两点的最短路
//rak[i][j]表示从i出发的最短路中第j小的到达点
int n;
ll center_point(int &u,int &v,double &x) //找图的绝对中心,返回绝对中心所在的边以及离边的u点的距离
//最后返回绝对中心距离的最小值的两倍
{
//floyd求最短路
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) dis[i][j] = min(dis[i][k]+dis[k][j],dis[i][j]);
//求rak数组
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++) rak[i][j] = j;
for (int j = 1; j <= n; j++)
for (int k = j+1; k <= n; k++)
if( dis[i][rak[i][j]] > dis[i][rak[i][k]] ) swap(rak[i][j],rak[i][k]);
}
ll ans = 1e18;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if( i == j || g[i][j] == 1e18 ) continue;
int id = rak[i][n];
ll tmp = dis[i][id] * 2;
if( ans > tmp )
{
ans = tmp;
u = i,v = j;
x = 0;
}
for (int k = n-1; k >= 1; k--)
{
int t = rak[i][k];
if( dis[j][t] > dis[j][id] )
{
ll tmp = dis[i][t] + dis[j][id] + g[i][j];
if( ans > tmp )
{
ans = tmp;
u = i,v = j;
x = (dis[j][id]+g[i][j]-dis[i][t])/2.0;
}
id = t;
}
}
}
}
return ans;
}
图的最小直径生成树
最小直径生成树就是在图上找到一棵生成树,使得这棵生成树的直径尽可能小。
分析: 首先先找到图的绝对中心,这样跑一次dij,就可以把树建出来了,树上的每条边都是最短路径。这样每个点到绝对中心的距离都保证了最小,而绝对中心又保证了到所有点的最短路的最大值最小。