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

链接:Qin Shi Huang's National Road System - HDU 4081 - Virtual Judge (vjudge.net)

题目大意:有n个城市,秦始皇要修用n-1条路把它们连起来,要求从任一点出发,都可以到达其它的任意点。秦始皇希望这所有n-1条路长度之和最短。然后徐福突然有冒出来,说是他有魔法,可以不用人力、财力就变出其中任意一条路出来。

秦始皇希望徐福能把要修的n-1条路中最长的那条变出来,但是徐福希望能把要求的人力数量最多的那条变出来。对于每条路所需要的人力,是指这条路连接的两个城市的人数之和。

最终,秦始皇给出了一个公式,A/B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n-2条路径长度之和,选使得A/B值最大的那条。
思路:先求出最小生成树,设其权值和为sum,然后枚举每一条边w[i][j],如果该条边在最小生成树上的话,A=i和j2个城市的人数之和,此时A/B= A/sum-w[i][j],如果该边不在最小生成树中,加入 该边后,原来的最小生成树一定会形成一个环,选取该环上除去新加入的边外最大的一条边删除,这里求次小生成树的精髓在于dfs()这个函数,d1[a][b]记录的是从点a出发到b点的所有边中最长的边的长度,d2[a][b]记录的是次大值,具体看代码吧。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#define x first
#define y second
using namespace std;
const int N =1010;
int p[N];
int h[N],idx,e[N<<1],ne[N<<1];
double w[N<<1];
struct node{
	int u,v;
	double w;
		bool f;
}edge[500010];
int val[N];
struct pode{
	int x,y;
}de[N];
int n;
double d1[N][N],d2[N][N];
int find(int x)
{
	if(x!=p[x])
	p[x]=find(p[x]);
	return p[x];
}
void add(int a,int b,double c)
{
	w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
double getdis(int x1,int y1,int x2,int y2)
{
	int dx=x1-x2;
	int dy=y1-y2;
	return sqrt(dx*1.0*dx+dy*1.0*dy);
}
double  dist(int a,int b)
{
	int dx=de[a].x-de[b].x;
	int dy=de[a].y-de[b].y;
	return sqrt(dx*1.0*dx+dy*1.0*dy);
}
bool cmp(struct node a,struct node b)
{
	return a.w<b.w;
}
void dfs(int u,int fa,double maxx1,double maxx2,double d1[],double d2[])
{
	d1[u]=maxx1,d2[u]=maxx2;
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa)continue;
		double td1=maxx1,td2=maxx2;
		if(w[i]>td1)td2=td1,td1=w[i];
		else if(w[i]<td1&&w[i]>td2)td2=w[i];
		dfs(j,u,td1,td2,d1,d2);
	}
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		memset(h,-1,sizeof h);
		idx=0;
		int m=n;
		for(int i=1;i<=n;i++)
			{
				int a,b;
				scanf("%d%d%d",&a,&b,&val[i]);
				p[i]=i;
				de[i]={a,b};
			}
		int cnt=0;
//		for(int i=1;i<=n;i++)
//			for(int j=1;j<=i;j++)
//			{
//				edge[++cnt].u=i;
//				edge[cnt].v=j;
				edge[cnt].w=getdis(de[i].x,de[i].y,de[j].x,de[j].y);	
//					edge[cnt].w=dist(i,j);		    
//			}
        for(int i=1;i<=n;i++)
			for(int j=1;j<=i;j++)
				edge[++cnt]={i,j,dist(i,j)};//这里就很迷,这样给结构体赋值就能AC,用上面注释的方法赋值就WA 
		sort(edge+1,edge+cnt+1,cmp);
		double sum=0;
		int num=0;
			for(int i=1;i<=cnt;i++)
		{
			int u=edge[i].u,v=edge[i].v;
			int fu=find(u),fv=find(v);
			double w=edge[i].w;
			if(fu!=fv)
			{
				p[fu]=fv;
				sum+=w;
				edge[i].f=1;
				add(u,v,w);
				add(v,u,w);
				if(--m==1)
				break;
			}
		}
		for(int i=1;i<=n;i++)
		dfs(i,0,-1e9,-1e9,d1[i],d2[i]);
		double res=-1e18;
        for(int i=1;i<=cnt;i++)
		{
			double temp;
			int a=edge[i].u,b=edge[i].v;
			double w=edge[i].w;
				int v=val[a]+val[b];
			if(edge[i].f)
			temp=v/(sum-w);
			else
			{		
				if(w>d1[a][b])
					temp=v/(sum-d1[a][b]);
				else if(w>d2[a][b])
					temp=v/(sum-d2[a][b]);	//这两种情况至少会满足一种,如果这2个条件都不满足的话,那这条边就一定在最小生成树中		
			}
			res=max(res,temp);
		}
		printf("%.2lf\n",res);		
	}
	return 0;
}

——————致我的2个夜晚和半个中午

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值