大意:
有n个城市,秦始皇要修用n-1条路把它们连起来,要求从任一点出发,都可以到达其它的任意点。秦始皇希望这所有n-1条路长度之和最短。然后徐福突然有冒出来,说是他有魔法,可以不用人力、财力就变出其中任意一条路出来。
秦始皇希望徐福能把要修的n-1条路中最长的那条变出来,但是徐福希望能把要求的人力数量最多的那条变出来。对于每条路所需要的人力,A是指这条路连接的两个城市的人数之和,对,是两个城市。
最终,秦始皇给出了一个公式,A/B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n-2条路径长度之和,选使得A/B值最大的那条。
思路:这道题可以看做是次小生成树的变形,我们可以通过枚举的方法求A/B的最大值。
为了使的A/B值最大,首先是需要是B尽量要小,所以可先求出n个城市的最小生成树。然后,就是决定要选择那一条用徐福的魔法来变。
1、枚举的边属于MST。如果这一条边已经是属于最小生成树上的,那么最终式子的值是A/(cnt-w[i][j])。
2、如果这一条不属于MST, 那么添加上这条边,就会有n条边,那么就会使得有了一个环,为了使得它还是一个生成树,就要删掉环上的一棵树。 为了让生成树尽量少,那么就要删掉除了加入的那条边以外,权值最大的那条路径。 假设删除的那个边的权值是Max[i][j],那么就是A/(cnt-Max[i][j]).
解这题的关键也在于怎样求出次小生成树,关于次小生成树,可以去网上搜搜资料,
POJ 1679 是不错的入门题,我的模板链接:http://www.cnblogs.com/g0feng/archive/2012/10/23/2735720.html
CODE:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
using namespace std;
#define MAXN 1100
#define INF 0X3F3F3F3F
double w[MAXN][MAXN], d[MAXN];
int use[MAXN][MAXN];
double Max[MAXN][MAXN];
int fa[MAXN], vis[MAXN], cost[MAXN];
int n, m;
double cnt;
struct node
{
double x, y;
}a[MAXN];
double dis(const node &a, const node &b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void init()
{
cnt = 0;
memset(use, 0, sizeof(use));
memset(w, INF, sizeof(w));
memset(vis, 0, sizeof(vis));
}
void Prim(int src)
{
for(int i = 1; i <= n; i++) d[i] = (i == src)? 0:INF;
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= n; i++)
{
int x;
double m = INF;
for(int y = 1; y <= n; y++) if(!vis[y] && d[y] < m) m = d[x=y];
for(int y = 1; y <= n; y++) if(vis[y])/*这是算法的关键,记录下vis[y]到k的路径中权值最大的值,用于替换处理*/
{
Max[y][x] = Max[x][y] = max(d[x], Max[y][fa[x]]);
}
vis[x] = 1;
use[x][fa[x]] = use[fa[x]][x] = 1; //标记在MST树中
cnt += m;
for(int y = 1; y <= n; y++)
{
if(!vis[y] && d[y] > w[x][y])
{
d[y] = w[x][y];
fa[y] = x; //记录父亲节点
}
}
}
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
init();
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%lf%lf%d", &a[i].x, &a[i].y, &cost[i]);
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++) if(i != j)
{
w[i][j] = w[j][i] = dis(a[i], a[j]);
}
}
Prim(1);
double ans = -1;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++) if(i != j)
{
if(use[i][j])
{
ans = max(ans, (cost[i]+cost[j])/(cnt-w[i][j]));
}
else
{
ans = max(ans, (cost[i]+cost[j])/(cnt-Max[i][j]));
}
}
}
printf("%.2lf\n", ans);
}
return 0;
}
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
using namespace std;
#define MAXN 1100
#define INF 0X3F3F3F3F
double w[MAXN][MAXN], d[MAXN];
int use[MAXN][MAXN];
double Max[MAXN][MAXN];
int fa[MAXN], vis[MAXN], cost[MAXN];
int n, m;
double cnt;
struct node
{
double x, y;
}a[MAXN];
double dis(const node &a, const node &b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
void init()
{
cnt = 0;
memset(use, 0, sizeof(use));
memset(w, INF, sizeof(w));
memset(vis, 0, sizeof(vis));
}
void Prim(int src)
{
for(int i = 1; i <= n; i++) d[i] = (i == src)? 0:INF;
for(int i = 1; i <= n; i++) fa[i] = i;
for(int i = 1; i <= n; i++)
{
int x;
double m = INF;
for(int y = 1; y <= n; y++) if(!vis[y] && d[y] < m) m = d[x=y];
for(int y = 1; y <= n; y++) if(vis[y])/*这是算法的关键,记录下vis[y]到k的路径中权值最大的值,用于替换处理*/
{
Max[y][x] = Max[x][y] = max(d[x], Max[y][fa[x]]);
}
vis[x] = 1;
use[x][fa[x]] = use[fa[x]][x] = 1; //标记在MST树中
cnt += m;
for(int y = 1; y <= n; y++)
{
if(!vis[y] && d[y] > w[x][y])
{
d[y] = w[x][y];
fa[y] = x; //记录父亲节点
}
}
}
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
init();
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%lf%lf%d", &a[i].x, &a[i].y, &cost[i]);
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++) if(i != j)
{
w[i][j] = w[j][i] = dis(a[i], a[j]);
}
}
Prim(1);
double ans = -1;
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++) if(i != j)
{
if(use[i][j])
{
ans = max(ans, (cost[i]+cost[j])/(cnt-w[i][j]));
}
else
{
ans = max(ans, (cost[i]+cost[j])/(cnt-Max[i][j]));
}
}
}
printf("%.2lf\n", ans);
}
return 0;
}