您的位置:首页 > 其它

hdu4081 Qin Shi Huang's National Road System--次小生成树

2016-08-08 15:13 351 查看
原题链接: http://acm.hdu.edu.cn/showproblem.php?pid=4081

题目大意:

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

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

最终,秦始皇给出了一个公式,A/B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n-2条路径长度之和,选使得A/B值最大的那条。

分析与总结

为了使的A/B值最大,首先是需要是B尽量要小,所以可先求出n个城市的最小生成树。然后,就是决定要选择那一条用徐福的魔法来变。

因此,可以枚举每一条边,假设最小生成树的值是MinMST, 而枚举的那条边长度是w[i][j],  如果这一条边已经是属于最小生成树上的,那么最终式子的值是A/(MinMST-w[i][j])。如果这一条不属于最小生成树上的, 那么添加上这条边,就会有n条边,那么就会使得有了一个环,为了使得它还是一个生成树,就要删掉环上的一棵树。 为了让生成树尽量少,那么就要删掉除了加入的那条边以外,权值最大的那条路径。 假设删除的那个边的权值是path[i][j], 那么就是A/(MinMST-path[i][j]).

解这题的关键也在于怎样求出次小生成树。

#define _CRT_SECURE_NO_DEPRECATE

#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>

using namespace std;

double dis[1005][1005];//两城市距离
double lowCost[1005];//
double maxx[1005][1005];//保存最小树上i到j上的最大距离
double node[1005][2];//坐标
double num[1005];//每个城市的人数
int pre[1005];//保存上一节点
bool used[1005][1005];//标记该边是否加入最小生成树
bool vis[1005];
double B;//生成树权值
int n;

double getDis(double x1, double y1, double x2, double y2)
{
return sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2));
}

void prim()
{
memset(used, 0, sizeof(used));
memset(vis, 0, sizeof(vis));

B = 0.0;
for (int i = 1; i <= n; i++)
{
lowCost[i] = dis[1][i];//默认1为起点
pre[i] = 1;
}
vis[1] = 1;

for (int i = 1; i < n; i++)
{
int minPos;
double minn = 99999999;
for (int j = 1; j <= n; j++)
{
if (!vis[j] && lowCost[j] < minn)
{
minn = lowCost[j];
minPos = j;
}
}

B += minn;
used[minPos][pre[minPos]] = used[pre[minPos]][minPos] = 1;
vis[minPos] = 1;

for (int j = 1; j <= n; j++)
{
if (minPos != j&&vis[j])//该点已加入生成树
maxx[j][minPos] = maxx[minPos][j] = max(maxx[j][pre[minPos]], lowCost[minPos]);

if (!vis[j] && lowCost[j] > dis[minPos][j])
{
lowCost[j] = dis[minPos][j];
pre[j] = minPos;
}

}

}
}

int main()
{
int T;

scanf("%d", &T);
while (T--)
{
scanf("%d", &n);

for (int i = 1; i <= n; i++)
scanf("%lf%lf%lf", &node[i][0], &node[i][1], &num[i]);

for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
if (i != j)
dis[i][j] = getDis(node[i][0], node[i][1], node[j][0], node[j][1]);

prim();

double ans = -1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (i != j)
{
if (used[i][j])
ans = max(ans, (num[i] + num[j]) / (B - dis[i][j]));
else
ans = max(ans, (num[i] + num[j]) / (B - maxx[i][j]));
}

}
}

printf("%.2lf\n", ans);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: