题目:秦始皇想要修路,他只想着修总长度最小(最小生成树)。但有个道士告诉他,可以用魔法修一条路,不耗费人力财力的一条魔法路。秦始皇想要使非魔法道路总长度尽可能小,道士希望尽可能多的人受益。最终决定:修魔法路的两个城市,(两个城市的总人口)/(非魔法道路总长度)必须是最大。
输入:t个测试数据,n个城市,每个城市输入为坐标(x,y)和人口p
输出:(两个城市的总人口)/(非魔法道路总长度)最大值
次小生成树:在最小生成树的基础上,枚举每一条不在最小生成树上的边,把边放到最小生成树上,然后就会形成环路,去掉环路上除了新加入的边的最长边,根据附加的条件得到要的答案。
具体实现:除了基本的最小生成树,还有加入connect数组用来记录不属于最小生成树的连通边(方便枚举每一条不在最小生成树上的边),maxd数组记录最小生成树中i点到j点中最大的距离(方便后面删去环路上除了新加入的边的最长边),per数组记录最小生成树里面每个点的上一个链接的点(方便得到父坐标)
代码:
#include <algorithm>
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1005;
const int inf=1e9+9;
int n;
bool vis[maxn];//记录最小生成树集合
double dis[maxn];//更新最小生成树集合到其他点的距离
double maxd[maxn][maxn];//maxd[i][j]表示最小生成树里i点到j点中最大的距离
int per[maxn];//最小生成树里面每个点的上一个链接的点
double connect[maxn][maxn];//记录是否是通路,且该通路不在最小生成树里面
struct nod
{
double x,y;
double p;
} num[maxn];
double calculate(int i,int j)//计算两点的距离
{
return sqrt((num[i].x-num[j].x)*(num[i].x-num[j].x)+(num[i].y-num[j].y)*(num[i].y-num[j].y));
}
double Prim()
{
memset(maxd,0,sizeof(maxd));
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)//选第一个点
{
dis[i]=calculate(1,i);
per[i]=1;
}
vis[1]=1;
double sum=0;
for(int i=1;i<n;i++)
{
double min1=inf;
int p=-1;
for(int j=1;j<=n;j++)//找出与最小生成树当前集合相连的最小距离的点
{
if(!vis[j]&&dis[j]<min1)
{
p=j;
min1=dis[j];
}
}
if(p==-1)//如果找不到了,就说明已经找完了
return sum;
vis[p]=1;
//把这条边从通路中删除,保留非生成树通路
connect[p][per[p]]=false;
connect[per[p]][p]=false;
sum+=min1;
maxd[per[p]][p]=maxd[p][per[p]]=min1;
for(int j=1;j<=n;j++)
{
if(j!=p&&vis[j])
{
maxd[p][j]=maxd[j][p]=max(maxd[j][per[p]],dis[p]);
}
if(!vis[j]&&calculate(j,p)<dis[j])
{
dis[j]=calculate(j,p);
per[j]=p;
}
}
}
return sum;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
//memset(mp,inf,sizeof(mp));
memset(connect,true,sizeof(connect));
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%lf%lf%lf",&num[i].x,&num[i].y,&num[i].p);
}
double sum=Prim();
double ans=-1;
for(int i=1;i<=n;i++)
{
for(int j=i+1;j<=n;j++)
{
if(connect[i][j])
{
ans=max(ans,1.0*(num[i].p+num[j].p)/(sum-maxd[i][j]));
}
else
{
ans=max(ans,1.0*(num[i].p+num[j].p)/(sum-calculate(i,j)));
}
}
}
printf("%.2lf\n",ans);
}
return 0;
}
总结:1 学到了新东西 2 看英文题的时候,不要太依赖网页翻译工具,还是要自己看看英文。(这次网页翻译出错,导致一直看错题)