题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4081
解析:次小生成树问题。因为求A/B,所以要求B尽量小,所以需要求出最小生成树(MST),枚举最小生成树中的所有边,然后就会变成两颗子树,求两颗子树中人口最大的点,则这两个点之间的边就是用魔法建造的边。
我是用prim求最小生成树,然后把最小生成树中的边存入e[]数组,最后用dfs来求两颗子树各自的最大人口点
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define INF 1100
#define Max 1000000000
#define fmax(x,y) x<y?y:x
#define MEM(x,y) memset(x,y,sizeof(x))
using namespace std;
struct point
{
int x,y,pop;
}p[INF];
struct edge
{
int x,next; //x存边指向的点,next存与该边同起点的边在边集中的下标
}e[INF*500]; //用来存最小生成树的图中的边
int n,cnt;
int vis[INF],low[INF]; //vis[]数组标记该点是否访问过,low[]代表该点到标记点集的距离
int map[INF][INF];
int head[INF],pre[INF]; //head[]用来存该点的最新的一条边在边集中的下标,pre[]存该点在MST中的前驱点
double sum;
void addedge(int x ,int y) //x是该边的起点,y是该边的终点
{
e[cnt].x = y;
e[cnt].next = head[x];
head[x] = cnt++;
}
double prim(int start) //求最小生成树
{
int pos,min;
double result = 0;
MEM(vis,0);
MEM(pre,0);
pre[start] = -1; //起始点没有前驱点
for(int i = 0 ; i <= INF; i ++)
low[i] = Max;
vis[start] = 1;
pos = 1; //pos记录当前应该从哪点开始更新
for(int i = 1; i <= n ; i ++) //初始化low[]数组
{
if(i != pos)
low[i] = map[start][i];
}
for(int i = 1; i < n;i++)
{
min = Max;
for(int j = 1; j <= n ; j ++) //求到标记点集距离最近的边和点
{
if(vis[j] == 0 && min > low[j])
{
min = low[j];
pos = j;
}
}
if(pre[pos] != -1) //把边加入最小生成树中
{
addedge(pre[pos],pos);
addedge(pos,pre[pos]);
}
result += sqrt(min);
vis[pos] = 1;
for(int j = 1; j <= n ; j ++) //更新权值
{
if(vis[j] == 0 && low[j] > map[pos][j])
{
low[j] = map[pos][j];
pre[j] = pos;
}
}
}
return result;
}
int m = 0; //m存储某棵树的城市的人口
void dfs(int x,int y) //x代表从x点开始递归,y点代表x->y这条边不能遍历
{
m = fmax(m,p[x].pop);
for(int i = head[x]; i != -1;i = e[i].next)
{
if(e[i].x == y)
continue;
dfs(e[i].x,x);
}
}
void solve()
{
double temp = 0;
double ans = 0;
for(int k = 1; k <= n ; k++) //枚举每个点,就相当于枚举最小生成树中的边
{
for(int i = head[k] ; i != -1 ; i = e[i].next) //枚举由k点作为起点的每一条边
{
temp = 0;
m = 0;
dfs(k,e[i].x); //求左边子树的点最大人口
temp += m;
m = 0;
dfs(e[i].x,k); //求右边子树点的最大人口
temp += m;
temp /= (sum-sqrt(map[k][e[i].x]*1.0));
if(ans < temp)
ans = temp;
}
}
printf("%.2lf\n",ans);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
MEM(p,0);
MEM(head,-1);
cnt = 0;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1; j <= n ; j ++)
map[i][j] = Max;
scanf("%d",&n);
for(int i = 1; i <= n ; i ++)
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].pop);
cnt= 0;
for(int i = 1 ; i <= n ; i ++)
for(int j = i+1; j <= n ; j ++)
map[i][j] = map[j][i] = (p[i].x -p[j].x )*(p[i].x-p[j].x)+(p[i].y-p[j].y)*(p[i].y-p[j].y); //先存距离的平方,后面再开平方
sum = prim(1); //求出最小生成树
solve();
}
return 0;
}