HDU4081 Qin Shi Huang's National Road System 【次小生成树】

Qin Shi Huang‘s National Road System

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 3395    Accepted Submission(s): 1189

Problem Description

During the Warring States Period of ancient China(476 BC to 221 BC), there were seven kingdoms in China ---- they were Qi, Chu, Yan, Han, Zhao, Wei and Qin. Ying Zheng was the king of the kingdom Qin. Through 9 years of wars, he finally
conquered all six other kingdoms and became the first emperor of a unified China in 221 BC. That was Qin dynasty ---- the first imperial dynasty of China(not to be confused with the Qing Dynasty, the last dynasty of China). So Ying Zheng named himself "Qin
Shi Huang" because "Shi Huang" means "the first emperor" in Chinese.

Qin Shi Huang undertook gigantic projects, including the first version of the Great Wall of China, the now famous city-sized mausoleum guarded by a life-sized Terracotta Army, and a massive national road system. There is a story about the road system:

There were n cities in China and Qin Shi Huang wanted them all be connected by n-1 roads, in order that he could go to every city from the capital city Xianyang.

Although Qin Shi Huang was a tyrant, he wanted the total length of all roads to be minimum,so that the road system may not cost too many people‘s life. A daoshi (some kind of monk) named Xu Fu told Qin Shi Huang that he could build a road by magic and that
magic road would cost no money and no labor. But Xu Fu could only build ONE magic road for Qin Shi Huang. So Qin Shi Huang had to decide where to build the magic road. Qin Shi Huang wanted the total length of all none magic roads to be as small as possible,
but Xu Fu wanted the magic road to benefit as many people as possible ---- So Qin Shi Huang decided that the value of A/B (the ratio of A to B) must be the maximum, which A is the total population of the two cites connected by the magic road, and B is the
total length of none magic roads.

Would you help Qin Shi Huang?

A city can be considered as a point, and a road can be considered as a line segment connecting two points.

Input

The first line contains an integer t meaning that there are t test cases(t <= 10).

For each test case:

The first line is an integer n meaning that there are n cities(2 < n <= 1000).

Then n lines follow. Each line contains three integers X, Y and P ( 0 <= X, Y <= 1000, 0 < P < 100000). (X, Y) is the coordinate of a city and P is the population of that city.

It is guaranteed that each city has a distinct location.

Output

For each test case, print a line indicating the above mentioned maximum ratio A/B. The result should be rounded to 2 digits after decimal point.

Sample Input

2
4
1 1 20
1 2 30
200 2 80
200 1 100
3
1 1 20
1 2 30
2 2 40

Sample Output

65.00
70.00

Source

2011 Asia Beijing Regional Contest

题意:给定n个点的点权及相互间的边权,求一棵树,其中一条边的边权变为0,树的比率值为该0值边所连的两点的点权和/剩下的树边和,求这个值最大是多少。

题解:这题要用到次小生成树的思想,即找到最小生成树,然后添加一条边构成环,再删掉环中属于最小树的最大边,用这种方法遍历所有边以找到最终ans。

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#define maxn 1002
#define maxm (maxn * maxn) >> 1
using std::sort;

struct Node{
	int u, v;
	double dist;
} E[maxm]; //存储边
struct Node2{
	int x, y, peo;
} V[maxn]; //存储顶点
int head[maxn], end[maxn]; //存储集合首尾
struct Node3{
	int u, next;
} G[maxn]; //链式前向星存集合归属信息
int pre[maxn]; //并查集
double max[maxn][maxn]; //存储最小树上的两点间最长的一条单元路

double maxv(double a, double b){
	return a > b ? a : b;
}

double calDist(int i, int j)
{
	double x = V[i].x - V[j].x;
	double y = V[i].y - V[j].y;
	return sqrt(x * x + y * y);
}

bool cmp(Node a, Node b){
	return a.dist < b.dist;
}

int ufind(int k)
{
	int a = k, b;
	while(pre[k] != -1) k = pre[k];
	while(a != k){
		b = pre[a];
		pre[a] = k;
		a = b;
	}
	return k;
}

double Kruskal(int n, int m)
{
	memset(pre, -1, sizeof(pre));
	int count = n, i, j, k, u, v;
	double len = 0, dist;
	for(i = 0; i < n; ++i){ //初始化每个点的集合只有其本身
		G[i].u = i; G[i].next = -1;
		head[i] = end[i] = i;
	}
	for(i = 0; i < m; ++i){
		u = E[i].u; v = E[i].v;
		dist = E[i].dist;
		u = ufind(u); v = ufind(v);
		if(u != v){
			for(j = head[u]; j != -1; j = G[j].next)
				for(k = head[v]; k != -1; k = G[k].next)
					max[G[j].u][G[k].u] = max[G[k].u][G[j].u] = dist;
			//合并集合
			G[end[v]].next = head[u]; head[u] = head[v];

			pre[v] = u; --count; len += dist;
			if(1 == count) break; //最小树生成
		}
	}
	return len;
}

int main()
{
	//freopen("stdin.txt", "r", stdin);
	int t, n, i, j, id;
	double minLen, ans;
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		for(i = 0; i < n; ++i)
			scanf("%d%d%d", &V[i].x, &V[i].y, &V[i].peo);
		for(i = id = 0; i < n; ++i)
			for(j = i + 1; j < n; ++j){
				E[id].u = i; E[id].v = j;
				E[id++].dist = calDist(i, j);
			}
		sort(E, E + id, cmp);
		minLen = Kruskal(n, id);
		ans = 0;
		for(i = 0; i < n; ++i) //枚举所有魔法边
			for(j = i + 1; j < n; ++j){
				ans = maxv(ans, (V[i].peo + V[j].peo) / (minLen - max[i][j]));
			}
		printf("%.2lf\n", ans);
	}
	return 0;
}

附之前写的一个用prim的超时代码:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#define maxn 1002

double map[maxn][maxn], max[maxn][maxn];
bool vis[maxn];
struct Node{
    int x, y, peo;
} node[maxn];

double calDist(int i, int j)
{
    int x = node[i].x - node[j].x;
    int y = node[i].y - node[j].y;
    return sqrt((double)(x * x + y * y));
}

double prim(int n)
{
    memset(vis, 0, sizeof(vis));
    int count = 0, i, j, v;
    double tmp, len = 0;
    vis[0] = 1;
    while(count < n - 1){
        for(i = 0, tmp = INT_MAX; i < n; ++i){
            if(!vis[i]) continue;
            for(j = 0; j < n; ++j){
                if(vis[j]) continue;
                if(map[i][j] < tmp){
                    tmp = map[i][j]; v = j;
                }
            }
        }
        for(i = 0; i < n; ++i)
        	if(vis[i]) max[i][v] = max[v][i] = tmp;
        ++count; vis[v] = 1; len += tmp;
    }
    return len;
}

double getAns(int n, double minLen)
{
	int i, j, peo;
	double ans = 0, tmp;
	for(i = 0; i < n; ++i)
		for(j = i + 1; j < n; ++j){
			tmp = (node[i].peo + node[j].peo) / (minLen - max[i][j]);
			if(tmp > ans) ans = tmp;
		}
	return ans;
}

int main()
{
    int t, n, i, j, peo;
    scanf("%d", &t);
    while(t--){
        scanf("%d", &n);
        for(i = 0; i < n; ++i)
            scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].peo);

        for(i = 0; i < n; ++i)
        	for(j = i + 1; j < n; ++j)
        	    map[i][j] = map[j][i] = calDist(i, j);

        printf("%.2lf\n", getAns(n, prim(n)));
    }
    return 0;
}

HDU4081 Qin Shi Huang's National Road System 【次小生成树】,布布扣,bubuko.com

HDU4081 Qin Shi Huang's National Road System 【次小生成树】

时间: 2024-10-25 21:36:48

HDU4081 Qin Shi Huang's National Road System 【次小生成树】的相关文章

hdu4081 Qin Shi Huang&#39;s National Road System 次小生成树

先发发牢骚:图论500题上说这题是最小生成树+DFS,网上搜题解也有人这么做.但是其实就是次小生成树.次小生成树完全当模版题.其中有一个小细节没注意,导致我几个小时一直在找错.有了模版要会用模版,然后慢慢融会贯通.我要走的路还长着啊. 讲讲次小生成树的思想吧.首先,在prim算法中做一些改变,求出任意两点(u,v)路径之间的最大权值,并记录,记为maxe[u][v].运行遍prim算法.枚举每一条边,如果该边是属于最小生成树的边,则ratio=(value(u) +value(v) ) /( a

hdu 4081 Qin Shi Huang&#39;s National Road System 次小生成树 算法

Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4180    Accepted Submission(s): 1450 Problem Description During the Warring States Period of ancient China(4

HDU 4081 Qin Shi Huang&#39;s National Road System 次小生成树

给你n个城市 每个城市有一定数量的人 连接2个城市需要的花费是他们之间的距离 现在要建一颗最小生成树 可以免费建其中一条边 设A为免费的那条边连接的2个城市的人口之和 B为修建的最小生成树的花费 求最大的A/B 先求最小生成树 设总花费为totol 然后可以枚举免费的那条边 枚举边等于A确定 在这条在最小生成树里的情况下 求最小的B 如果这条边是最小生成树里的边 那么很容易求得B 拿totol减去这条边就行了 如果不是 那么把这条边加到最小生成树 会出现一个环 找到这个环最大的那条边剪掉 就是次

HDU 4081 Qin Shi Huang&#39;s National Road System 次小生成树变种

Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) [Problem Description] During the Warring States Period of ancient China(476 BC to 221 BC), there were seven kingdoms in China ---

HDU4081 Qin Shi Huang&#39;s National Road System【Kruska】【次小生成树】

Qin Shi Huang's National Road System Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3979    Accepted Submission(s): 1372 Problem Description During the Warring States Period of ancient China(4

HDU4081 Qin Shi Huang&#39;s National Road System(次小生成树)

枚举作为magic road的边,然后求出A/B. A/B得在大概O(1)的时间复杂度求出,关键是B,B是包含magic road的最小生成树. 这么求得: 先在原图求MST,边总和记为s,顺便求出MST上任意两点路径上的最长边d[i][j]. 当(u,v)是magic road时, 如果它在原本的MST上,则B就等于s-原(u,v)的权,而原(u,v)的权其实就是d[u][v]: 如果它不在原本的MST上,则B就等于s-d[u][v]+0. 总之就是一个式子:B=s-d[u][v]. 于是,在

HDU4081 Qin Shi Huang&#39;s National Road System【prim最小生成树+枚举】

先求出最小生成树,然后枚举树上的边,对于每条边"分别"找出这条割边形成的两个块中点权最大的两个 1.由于结果是A/B,A的变化会引起B的变化,两个制约,无法直接贪心出最大的A/B,故要通过枚举 2.不管magic road要加在哪里,加的边是否是最小生成树上的边,都会产生环,我们都要选择一条边删掉 注意删掉的边必须是树的环上的边,为了使结果最大,即找出最大的边 3.可以枚举两点,找出边,也可以枚举边,找出点,我是用后者,感觉比较容易写也好理解 #include <cstdio&g

HDU-4081 Qin Shi Huang&#39;s National Road System(最小生成树)

今天比赛AC的一道最小生成树的题目 , 学到了不少东西 . 最小生成树的模板很简单,最简洁好写的还是lrj紫书上的代码 .利用并查集加速算法 . 该题的不同之处在于它选择任意一条路修成"魔法"道路 , 然后其他路的权值之和还要是最小的一棵次小生成树,并且求魔法道路两端点值之和除以其他路径长之和的最大值 . 显然该题的难点在于枚举两个端点之后怎么快速的求出次小生成树权值之和 .  枚举两个端点之后的复杂度已经是O(n^2),所以要想出一个快速的方法 . 受紫书上例题uva 1151 (传

HDU4081 Qin Shi Huang&#39;s National Road System

这个题还是比较好的,提供了一种求最小生成树不错的方法,但是,C++ TLE,G++  400+ms 1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 const int maxn = 1005; 7 struct edge 8 { 9 int v,next; 10 double w; 11 }e