标签:follow 技术分享 但我 计算 一个 acm mini otto code
Qin Shi Huang‘s National Road System
Input
Output
Sample Input
Sample Output
Source
题意:秦始皇要在n个城市之间修路,他想使得n个城市构成一个树形结构,并使得连接的路的长度和最小。这时,徐福说他有一种办法使得一条路不需要任何花销就能直接修成。秦始皇想让他修这些必要的路中最长的一条,但是徐福想修造福人口数最多的一条(即该条路连接的两个城市的人口和)。于是秦始皇为了均衡矛盾,给出了一种计算方法,将一条路两端的人口数A除以其他(n-2)条需要建造的道路的总长B,即计算A/B的比值,选取比值最大的一条修。输出该条路的比值。简而言之,该题就是要求除了徐福变的那条边既可以在秦始皇原定道路上,也可以不在,其他边都要在秦始皇原定道路上,然后要使徐福变的那条路A/B的值最大。
题解:看到秦始皇的要求,我们很自然就能想到最小生成树。所以我们先用prim求最小生成树。
为什么要用prim求最小生成树而不是kruskal呢?具体原因会在后面分析。
很显然,因为该题给出的是城市的坐标。所以我们会把每条路的长度首先都算出来,即算出了n(n-1)/2条边的长度,于是我们构成了一张完全图。我们是用这张完全图来求最小生成树的。
我们会发现,根据n的范围,完全图最多有499500条边,用kruskal也不可能TLE,但我们为什么要使用prim呢?
因为这题并不是一道裸的最小生成树,我们中间需要记录一些东西,用prim比较方便,所以不用kruskal。
我们要使A/B的值最小,可以想到如果在A固定的情况下,我们尽量使B小,于是我们有了下面的思路。
当我们求出最小生成树以后,设其值为MST,我们枚举完全图的每一条边。此时分两种情况讨论:
1、若枚举到的这条边在最小生成树上,设这条边是(i,j),我们直接计算(p[i]+p[j])/(MST-e[i][j]),即直接把这条路砍掉,按题意求值。
2、若枚举到的这条边不在最小生成树上,设这条边是(i,j),我们此时计算(p[i]+p[j])/(MST-g[i][j])。其中,g[i][j]表示从i到j的路径上最长的一条边是多少。
对于第2种情况,它的含义是我们将最小生成树上i到j的路径上最长的边砍掉,直接连上i到j的这条路径,然后这条路径由徐福的魔法来变。这样保证了道路还是树形结构,非魔法边是秦始皇的原定边,又能让这个式子的值尽量大。
通过以上分析,这题的具体算法也就明确了。接下来,我们只需要说明一下g[i][j]的求法此题就解决了。
对于g[i][j]的计算,我们可以在每一次加入一个新的点u时,计算g[u][j]=g[j][u]=max(g[pre[u]][j],dis[u]),当然j应该满足已经加入到最小生成树上。其中pre[u]表示扩展出u的前一个节点(我们如果把最小生成树看成以1为根节点的有根树,则pre[u]为u的父节点)。
同时,我们每次加入一个新节点u时,把一个bool型数组used[u][pre[u]]记为1,其他都为0,表示这条边是被最小生成树使用的。
综上所述,我们不难发现这些操作用prim算法操作比较简便,使用kruskal就没有这种特性。
再想看具体实现的见代码。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=1005; 4 int t,n,p[N],pre[N]; 5 double ans,MST,e[N][N],g[N][N],x[N],y[N],dis[N]; 6 bool vis[N],used[N][N]; 7 double get_dis(double a,double b,double c,double d){ 8 return sqrt((a-c)*(a-c)+(b-d)*(b-d)); 9 } 10 void prim() 11 { 12 memset(vis,0,sizeof(vis)); 13 memset(used,0,sizeof(used)); 14 memset(g,0,sizeof(g)); 15 for (int i=1;i<=n;++i) 16 dis[i]=e[1][i],pre[i]=1; 17 for (int i=1;i<=n;++i) 18 { 19 int u=-1; 20 for (int j=1;j<=n;++j) 21 if (!vis[j]&&(u==-1||dis[j]<dis[u])) u=j; 22 MST+=dis[u]; vis[u]=1; used[u][pre[u]]=used[pre[u]][u]=1; 23 for (int j=1;j<=n;++j) 24 { 25 if (vis[j]&&j!=u) g[u][j]=g[j][u]=max(g[j][pre[u]],dis[u]); 26 if (!vis[j]&&e[u][j]<dis[j]) dis[j]=e[u][j],pre[j]=u; 27 } 28 } 29 } 30 int main() 31 { 32 scanf("%d",&t); 33 while (t--) 34 { 35 scanf("%d",&n); 36 for (int i=1;i<=n;++i) 37 scanf("%lf%lf%d",&x[i],&y[i],&p[i]); 38 for (int i=1;i<n;++i) 39 for (int j=i+1;j<=n;++j) 40 e[i][j]=e[j][i]=get_dis(x[i],y[i],x[j],y[j]); 41 MST=0; ans=-1; prim(); 42 for (int i=1;i<n;++i) 43 for (int j=i+1;j<=n;++j) 44 if (used[i][j]) ans=max(ans,(p[i]+p[j])/(MST-e[i][j])); 45 else ans=max(ans,(p[i]+p[j])/(MST-g[i][j])); 46 printf("%0.2lf\n",ans); 47 } 48 return 0; 49 }
HDU 4081 Qin Shi Huang's National Road System
标签:follow 技术分享 但我 计算 一个 acm mini otto code
原文地址:http://www.cnblogs.com/zk1431043937/p/7751599.html