HDU 4081 Qin Shi Huang's National Road System(次小生成树)

题意:
给出n个城市,每个城市有坐标(x[i],y[i])和人数p[i],秦始皇想用n-1条道路把这n个城市连接起来,要求任意两个城市能互相到达,并且长度和最小,此时有一个道士说他可以用魔法修路,让其中一条路不需要花费钱和劳力。一方面秦始皇想让总长度尽可能小,另一方面道士想尽可能让更多的人受益魔法道路—所以秦世皇决定A/B的值必须是最大的,A是由魔法道路连接的两个城市的总人口,B是非魔法道路的总长度。


题解:
对于任意一条边,我们可以知道它的A值肯定是固定的,所以我们需要让B值尽可能小,才能让A/B更大,所以我们要求的就是包含u->v这条边的最小生成树。

我们可以先求出原图的最小生成树,如果u->v这条边在最小生成树中,那么直接求比例即可;如果不在最小生成树中,那么当我们把这条边加入以后肯定会形成一个环,用mx[i][j]表示u点到v点路径上的边权的最大值,然后只需要删除环中的最大边权即可


AC代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
#define LL long long
const int MAXN = 1000+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
int n,m,T,tot,pre[MAXN],vis[MAXN*MAXN/2],x[MAXN],y[MAXN],p[MAXN];
struct node{ int u,v; double w; }edge[MAXN*MAXN/2];
vector<int> g[MAXN]; double mx[MAXN][MAXN];
inline bool cmp(node x,node y){ return x.w<y.w; }
int Find(int x){ return pre[x]==x ? pre[x]:pre[x]=Find(pre[x]); }
inline double dis(int x,int y,int xx,int yy){
	return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
inline double Kruskal(){
	sort(edge+1,edge+tot+1,cmp);
	int cnt=0; double res=0;
	for(int i=1;i<=tot;i++){
		int xx=Find(edge[i].u),yy=Find(edge[i].v); double w=edge[i].w;
		if(xx!=yy){
			cnt++; vis[i]=true; res+=w;
			for(int j=0;j<g[xx].size();j++)
				for(int k=0;k<g[yy].size();k++)
					mx[g[xx][j]][g[yy][k]] = mx[g[yy][k]][g[xx][j]] = w;
			pre[xx]=yy;
			for(int j=0;j<g[xx].size();j++)
				g[yy].push_back(g[xx][j]);
		}
		if(cnt==n-1) return res;
	}
	return res;
}
signed main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d",&n); tot=0;
		for(int i=1;i<=n;i++) g[i].clear(),g[i].push_back(i),pre[i]=i;
		for(int i=1;i<=n;i++) scanf("%d%d%d",&x[i],&y[i],&p[i]);
		for(int i=1;i<=n;i++)
			for(int j=i+1;j<=n;j++)
				edge[++tot]=node{i,j,dis(x[i],y[i],x[j],y[j])},vis[tot]=false;
		double MST=Kruskal(),res=0;
		for(int i=1;i<=tot;i++){
			int u=edge[i].u,v=edge[i].v; double w=edge[i].w;
			if(!vis[i]) res = max(res,1.0*(p[u]+p[v])/(MST-mx[u][v]));
			else res = max(res,1.0*(p[u]+p[v])/(MST-w));
		}
		printf("%.2f\n",res);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值