题意:
秦始皇要修建连通所有城市的路,一共有N个点以及各个点的坐标和人口。修建很多条路使N个城市连通,但是可以使其中一条路的修建费用为0。在修建费用最小的前提下,使得A/B最大。A指费用为0的路所连通的两个城市的人口和,B指除修建费用为0的路之外的所以路之和。
解题思路:因为要是整体的修建费用最小,必定要求他的最小生成树。但是可以使生成树中的一条边的费用为0,这样就不能简单的光求最小生成树。因为可以使连接某两个城市的路修建费用为0,所以可以枚举所有两个城市的组合。这样A的值就固定下来了,为了使得A/B最大。则需要删去这两个城市的连通路径中最长的边,使得B值越小。因为在这两个城市直接连边是不需要费用的。这样的话就和求次小生成树的算法有点像了。一开始把问题像的简单了,所以直接用了kruskal。但是求次小生成树还是prime算法比较方便。
这里是关于次小生成树的介绍:http://blog.csdn.net/qq_31607947/article/details/77563793
看别人的代码蛮复杂,实际上就是 一次prim同时记录每两个点在树上的边中最大的那条,然后对于每两个点遍历,删边加边
double坑了我好几次wa
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
using namespace std;
const int maxn = 1011;
const double inf = 0x3f3f3f3f;
double Map[maxn][maxn];
double Max[maxn][maxn];
int pre[maxn];
double dis[maxn];
int people[maxn],x[maxn],y[maxn];
bool vis[maxn];
double prim(int n)
{
double ans = 0;
memset(vis, false, sizeof(vis));
memset(Max, 0, sizeof(Max));
for (int i = 2; i <= n; i++)
{
dis[i] = Map[1][i];
pre[i] = 1;
}
pre[1] = 0;
dis[1] = 0;
vis[1] = true;
for (int i = 2; i <= n; i++)
{
double min_dis = inf;
int k;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && min_dis > dis[j])
{
min_dis = dis[j];
k = j;
}
}
ans += min_dis;
vis[k] = true;
for (int j = 1; j <= n; j++)
{
if (vis[j]&&j!=k)
Max[j][k] = Max[k][j] = max(Max[j][pre[k]], dis[k]);
if (!vis[j] && dis[j] > Map[k][j])
{
dis[j] = Map[k][j];
pre[j] = k;
}
}
}
return ans;
}
double smst(int n, double min_ans)
{
double ans = 0;
for (int i = 1; i <= n; i++)//枚举最小生成树之外的边
for (int j = i + 1; j <= n; j++)
ans = max(ans, (people[i]+people[j])/((min_ans-Max[i][j])*1.0));
return ans;
}
int main()
{
int T, n;
scanf("%d", &T);
while (T--)
{
scanf("%d",&n);
for (int i = 1; i <=n; i++)
scanf("%d %d %d", &x[i], &y[i], &people[i]);
for (int i = 1;i<=n;i++)
for(int j=i;j<=n;j++)
Map[i][j]=Map[j][i]=sqrt(double((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]- y[j])));
double ans = prim(n);
printf("%.2lf\n",smst(n, ans));
}
return 0;
}
/*****************
本来准备放弃了回去没想到临走前改了double就过了
昨晚就睡了四个小时希望一会可以早早的睡着,最近失眠有点重