zoukankan      html  css  js  c++  java
  • [题解] uvalive 5713 Qin Shi Huang's National Road System 秦始皇修路(prim最小生成树)

    - 传送门 -

     https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3714

    5713 - Qin Shi Huang's National Road System

    Time limit: 3.000 seconds
    | Root | Submit Problem Stats
    uDebug Download as PDF |

    (题面见pdf)

     

    - 题意 -

     给出 n 个城市的坐标, 权值, 始皇要修 n-1 条路将所有城市连起来, 徐福可以无偿修一条路, 我们记这条路两端的城市权值和为 A , 剩下的路长度和为 B , 求max (A/B).
     (多组数据, 保留 2 位小数)
     

    - 思路 -

     先建最小生成树, 枚举树上点对(x, y), 连接x,y, 设 x - y 是徐福修的路, 然后在边x - y与树形成的环中切掉一条次大边(x - y最大)(即原树上x-y最小瓶颈路上最大边), 维护答案.
     
     以上是各大题解的糟糕的复述.
     
     然而我想了很久为什么确定了徐福修的路后, 剩下的边一定在最小生成树上, 发现可以用kruskal理解.
     假设现在只有x - y 这条边存在, 我们要接着再建一颗最小生成树, 这样就得到了确定某对点对后的最优解, 按kruskal依次将边从小到大加入图中, 得到的一定是原来的最小生成树少了之前提到的切掉的边, 因为在加入那条边时它的两端已经在一个并查集里了, 而其他步骤都和建原来的最小生成树一样.
     
     因为点数不大, 这里用到了暴搜维护最小瓶颈路上最大边.
     
     细节见代码.
     

    - 代码 -

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    using namespace std;
    
    typedef double db;
    const int N = 1000 + 5;
    const int INF = 0x7fffffff;
    
    double MA[N][N], MP[N][N], DIS[N], ans, tot;
    int TO[N << 1], NXT[N << 1], HD[N];
    int VIS[N], PRE[N];
    int X[N], Y[N], V[N];
    int T, n, sz;
    
    template <typename ty> ty Max(ty a, ty b) { return a > b ? a : b; }
    
    db calc(int x1, int x2, int y1, int y2) {
    	return sqrt((x1 - x2) * (x1 - x2) * 1.0 + (y1 - y2) * (y1 - y2) * 1.0);
    }
    
    void init() {
    	sz = 0;
    	ans = tot = 0;
    	memset(HD, 0, sizeof(HD));
    }
    
    void add(int x, int y, int val) {
    	TO[++sz] = y; NXT[sz] = HD[x]; HD[x] = sz;
    }
    
    void prim() {
    	for (int i = 1; i <= n; ++i) {
    		DIS[i] = MP[i][1];
    		VIS[i] = 0;
    		PRE[i] = 1;
    	}
    	PRE[1] = -1;
    	VIS[1] = 1;
    	for (int i = 2, k; i <= n; ++i) {
    		db minn = INF;
    		for (int j = 2; j <= n; ++j)
    			if (!VIS[j] && DIS[j] < minn) {
    				minn = DIS[j];
    				k = j;
    			}
    		tot += DIS[k];
    		VIS[k] = 1;
    		for (int j = 2; j <= n; ++j)
    			if (!VIS[j] && DIS[j] > MP[k][j]) {
    				DIS[j] = MP[k][j];
    				PRE[j] = k;
    			}
    	}
    	for (int i = 2; i <= n; ++i) {
    		add(i, PRE[i], MP[i][PRE[i]]); add(PRE[i], i, MP[i][PRE[i]]);
    	}
    }
    
    void dfs(int x, int y, db v) {
    	MA[x][y] = MA[y][x] = v;
    	for (int i = HD[y]; i; i = NXT[i])
    		if (!VIS[TO[i]]) {
    			VIS[TO[i]] = 1;
    			dfs(x, TO[i], Max(v, MP[y][TO[i]])); //维护最小瓶颈路最大边
    		}
    }
    
    int main() {
    	scanf("%d", &T);
    	while (T--) {
    		init();
    		scanf("%d", &n);
    		for (int i = 1, x, y, v; i <= n; ++i)
    			scanf("%d%d%d", &X[i], &Y[i], &V[i]);
    		for (int i = 1; i <= n; ++i)
    			for (int j = 1; j <= n; ++j)
    				MP[i][j] = MP[j][i] = calc(X[i], X[j], Y[i], Y[j]);
    		prim();
    		for (int i = 1; i <= n; ++i) {
    			memset(VIS, 0, sizeof (VIS));
    			VIS[i] = 1;
    			dfs(i, i, 0);
    		}
    		for (int i = 1; i <= n; ++i)
    			for (int j = 1; j <= n; ++j) {
    				if (i == j) continue;
    				ans = Max(ans, (V[i] + V[j]) * 1.0 / (tot - MA[i][j]));
    			}
    		printf("%.2lf
    ", ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    三点定位
    vue中使用UEditor编辑器 -- 2
    网络编程——socket编程
    异常处理
    面向对象进阶
    初识面向对象,面向对象之继承、多态和封装
    Python常用的模块
    函数递归,匿名、内置行数,模块和包,开发规范
    闭包、装饰器decorator、迭代器与生成器、面向过程编程、三元表达式、列表解析与生成器表达式
    Python基础学习——文件操作、函数
  • 原文地址:https://www.cnblogs.com/Anding-16/p/7353777.html
Copyright © 2011-2022 走看看