HDU 4081 Qin Shi Huang's National Road System (次小生成树算法)

2023-12-10 04:48

本文主要是介绍HDU 4081 Qin Shi Huang's National Road System (次小生成树算法),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4081


题目:

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个城市,秦始皇要修用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]).

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


以下摘自网上资料:

[次小生成树]

类比上述次短路径求法,很容易想到一个“枚举删除最小生成树上的每条边,再求最小生成树”的直观解法。如果用Prim+堆,每次最小生成树时间复杂度为O(N*log(N+M) + M),枚举删除有O(N)条边,时间复杂度就是O(N^2*log(N+M) + N*M),当图很稠密时,接近O(N^3)。这种方法简易直观,但我们有一个更简单,而且效率更高的O(N^2+M)的解法,下面介绍这种方法。

首先求出原图最小生成树,记录权值之和为MinST。枚举添加每条不在最小生成树上的边(u,v),加上以后一定会形成一个环。找到环上权值第二大的边(即除了(u,v)以外的权值最大的边),把它删掉,计算当前生成树的权值之和。取所有枚举修改的生成树权值之和的最小值,就是次小生成树。

具体实现时,更简单的方法是从每个节点i遍历整个最小生成树,定义F[j]为从i到j的路径上最大边的权值。遍历图求出F[j]的值,然后对于添加每条不在最小生成树中的边(i,j),新的生成树权值之和就是MinST + w(i,j) – F[j],记录其最小值,则为次小生成树。

该算法的时间复杂度为O(N^2 + M)。由于只用求一次最小生成树,可以用最简单的Prim,时间复杂度为O(N^2)。算法的瓶颈不在求最小生成树,而在O(N^2+M)的枚举加边修改,所以用更好的最小生成树算法是没有必要的。


求解次小生成树的算法:

约定:由T 进行一次可行交换得到的新的生成树所组成的集合,称为树T的邻集,记为N(T)。
定理 3:设T是图G的最小生成树,如果T1满足ω(T1)=min{ω(T’)| T’∈N(T)},则T1是G
的次小生成树。
证明:如果 T1 不是G 的次小生成树,那么必定存在另一个生成树T’,T’=T 使得
ω(T)≤ω(T’)<ω(T1),由T1的定义式知T不属于N(T),则
E(T’)\E(T)={a1,a2
1,……,at},E(T)\E(T’)={b1,b2,……,bt},其中t≥2。根据引理1 知,存在一
个排列bi1,bi2,……,bit,使得T+aj-bij仍然是G 的生成树,且均属于N(T),所以ω(aj)≥ω(bij),
所以ω(T’)≥ω(T+aj-bij)≥ω(T1),故矛盾。所以T1是图G 的次小生成树。
通过上述定理,我们就有了解决次小生成树问题的基本思路。
首先先求该图的最小生成树T。时间复杂度O(Vlog2V+E)
然后,求T的邻集中权值和最小的生成树,即图G 的次小生成树。
如果只是简单的枚举,复杂度很高。首先枚举两条边的复杂度是O(VE),再判断该交换是否
可行的复杂度是O(V),则总的时间复杂度是O(V2E)。这样的算法显得很盲目。经过简单的
分析不难发现,每加入一条不在树上的边,总能形成一个环,只有删去环上的一条边,才能
保证交换后仍然是生成树,而删去边的权值越大,新得到的生成树的权值和越小。我们可以
以此将复杂度降为O(VE)。这已经前进了一大步,但仍不够好。
回顾上一个模型——最小度限制生成树,我们也曾面临过类似的问题,并且最终采用动态规
划的方法避免了重复计算,使得复杂度大大降低。对于本题,我们可以采用类似的思想。首
先做一步预处理,求出树上每两个结点之间的路径上的权值最大的边,然后,枚举图中不在
树上的边,有了刚才的预处理,我们就可以用O(1)的时间得到形成的环上的权值最大的边。
如何预处理呢?因为这是一棵树,所以并不需要什么高深的算法,只要简单的BFS 即可。
预处理所要的时间复杂度为O(V2)。
这样,这一步时间复杂度降为O(V2)。
综上所述,次小生成树的时间复杂度为O(V2)。

结论1
     次小生成树可由最小生成树换一条边得到.

证明:
    可以证明下面一个强一些的结论:
    T是某一棵最小生成树,T0是任一棵异于T的树,通过变换
T0 --> T1 --> T2 --> ... --> Tn (T)  变成最小生成树.
所谓的变换是,每次把Ti中的某条边换成T中的一条边, 而
且树T(i+1)的权小于等于Ti的权.
    具体操作是:
    step 1. 在Ti中任取一条不在T中的边uv.
    step 2. 把边uv去掉,就剩下两个连通分量A和B,
            在T中,必有唯一的边u'v' 连结A和B.
    step 3. 显然u'v'的权比uv小 (否则,uv就应该在T中).
            把u'v'替换uv即得树T(i+1).
    特别地:取T0为任一棵次小生成树,T(n-1) 也就是次小生成树且
    跟T差一条边. 结论1得证.

算法:
    只要充分利用结论1, 即得V^2的算法. 具体如下:

step 1.  先用prim求出最小生成树T.
         在prim的同时,用一个矩阵max[u][v] 记录 在T中连结任意两点u,v的唯一的
         路中权值最大的那条边的权值. (注意这里).
         这是很容易做到的,因为prim是每次增加一个结点s, 而设已经标号了的结点
         集合为W, 则W中所有的结点到s的路中的最大权值的边就是当前加入的这条边.
         step 1 用时 O(V^2).
step 2.  枚举所有不在T中的边uv, 加入边uv则必然替换权为max[u][v]的边.

故总时间为O(V^2).





代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define INF 2147483647
#define N 1005
using namespace std;double G[N][N],  minCost[N], pos[N][2], path[N][N], cost[N], ratio, A, B;
int pre[N], hash[N], n;
bool used[N][N];inline double getDist(double x1,double y1,double x2,double y2){return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}double Prim(){A=B=0;memset(hash, 0, sizeof(hash));memset(used, 0, sizeof(used));memset(path, 0, sizeof(path));hash[1]=1;for(int i=1; i<=n; ++i){minCost[i] = G[1][i];pre[i] = 1;}for(int i=1; i<n; ++i){int u=-1;for(int j=1; j<=n; ++j)if(!hash[j]){if(u==-1 || minCost[j]<minCost[u])u = j;}used[u][pre[u]]=used[pre[u]][u] = true;B += G[pre[u]][u];hash[u] = 1;for(int j=1; j<=n; ++j){if(hash[j]&&j!=u){path[u][j]=path[j][u]=max(path[j][pre[u]], minCost[u]); } if(!hash[j]){if(minCost[j]>G[u][j]){minCost[j] = G[u][j];pre[j] = u;}}}}return B;
}int main(){int T;scanf("%d",&T);while(T--){scanf("%d",&n);memset(G, 0, sizeof(G));for(int i=1; i<=n; ++i)scanf("%lf%lf%lf",&pos[i][0],&pos[i][1],&cost[i]);for(int i=1; i<=n; ++i){for(int j=1; j<=n; ++j)if(i!=j){G[i][j] = getDist(pos[i][0],pos[i][1],pos[j][0],pos[j][1]);}}Prim();ratio = -1;for(int i=1; i<=n; ++i){for(int j=1; j<=n; ++j)if(i!=j){if(used[i][j]){ratio = max(ratio, (cost[i]+cost[j])/(B-G[i][j]));}else{ratio = max(ratio, (cost[i]+cost[j])/(B-path[i][j]));}}}printf("%.2f\n", ratio);}return 0;
}


——  生命的意义,在于赋予它意义。

          
     原创 http://blog.csdn.net/shuangde800 , By   D_Double  (转载请标明)




这篇关于HDU 4081 Qin Shi Huang's National Road System (次小生成树算法)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/476056

相关文章

nginx生成自签名SSL证书配置HTTPS的实现

《nginx生成自签名SSL证书配置HTTPS的实现》本文主要介绍在Nginx中生成自签名SSL证书并配置HTTPS,包括安装Nginx、创建证书、配置证书以及测试访问,具有一定的参考价值,感兴趣的可... 目录一、安装nginx二、创建证书三、配置证书并验证四、测试一、安装nginxnginx必须有"-

Java实战之利用POI生成Excel图表

《Java实战之利用POI生成Excel图表》ApachePOI是Java生态中处理Office文档的核心工具,这篇文章主要为大家详细介绍了如何在Excel中创建折线图,柱状图,饼图等常见图表,需要的... 目录一、环境配置与依赖管理二、数据源准备与工作表构建三、图表生成核心步骤1. 折线图(Line Ch

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

浅析如何使用Swagger生成带权限控制的API文档

《浅析如何使用Swagger生成带权限控制的API文档》当涉及到权限控制时,如何生成既安全又详细的API文档就成了一个关键问题,所以这篇文章小编就来和大家好好聊聊如何用Swagger来生成带有... 目录准备工作配置 Swagger权限控制给 API 加上权限注解查看文档注意事项在咱们的开发工作里,API

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

Oracle数据库如何切换登录用户(system和sys)

《Oracle数据库如何切换登录用户(system和sys)》文章介绍了如何使用SQL*Plus工具登录Oracle数据库的system用户,包括打开登录入口、输入用户名和口令、以及切换到sys用户的... 目录打开登录入口登录system用户总结打开登录入口win+R打开运行对话框,输php入:sqlp

MybatisGenerator文件生成不出对应文件的问题

《MybatisGenerator文件生成不出对应文件的问题》本文介绍了使用MybatisGenerator生成文件时遇到的问题及解决方法,主要步骤包括检查目标表是否存在、是否能连接到数据库、配置生成... 目录MyBATisGenerator 文件生成不出对应文件先在项目结构里引入“targetProje

Python使用qrcode库实现生成二维码的操作指南

《Python使用qrcode库实现生成二维码的操作指南》二维码是一种广泛使用的二维条码,因其高效的数据存储能力和易于扫描的特点,广泛应用于支付、身份验证、营销推广等领域,Pythonqrcode库是... 目录一、安装 python qrcode 库二、基本使用方法1. 生成简单二维码2. 生成带 Log

Python中的随机森林算法与实战

《Python中的随机森林算法与实战》本文详细介绍了随机森林算法,包括其原理、实现步骤、分类和回归案例,并讨论了其优点和缺点,通过面向对象编程实现了一个简单的随机森林模型,并应用于鸢尾花分类和波士顿房... 目录1、随机森林算法概述2、随机森林的原理3、实现步骤4、分类案例:使用随机森林预测鸢尾花品种4.1