图的绝对中心与最小直径生成树

图的绝对中心
在图中找到一个点,使得它到所有其他点的最短路径的最大值尽可能小。
首先绝对中心到最远的两个点的距离一定是相等的。我们直接枚举每条边(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,就可以把树建出来了,树上的每条边都是最短路径。这样每个点到绝对中心的距离都保证了最小,而绝对中心又保证了到所有点的最短路的最大值最小。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值