Hdu Qin Shi Huang's National Road System 【次小生成树变形】

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

题目大意:在一个完全图中找一棵生成树,满足其中一条边的两个邻接点的点权之和除以该生成树的所有比安全和的比例最大,求该比例。

设最终所要的比例为ratio,则初始是设ratio为一个小负数。首先计算出图的最小生成树,设最小生成树的总长度为ans;然后枚举所有的点对(i,j),如果边(i,j)在最小生成树中,则比较 ratio 和 (cost[i]+cost[j])/(ans -G[i][j]) 的大小,否则,比较 ratio 和 (cost[i]+cost[j])/(ans -path[i][j]) 的大小;其中path[i][j]表示当最小生成树中加入了一条边(i,j)后,则形成了一个环,于是必须要在环中删除一条最长的非(i,j)的边,path[i][j]记录的就是这条边。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <queue>
using namespace std;
template <class T> void checkmin(T &t,T x) {if(x < t) t = x;}
template <class T> void checkmax(T &t,T x) {if(x > t) t = x;}
template <class T> void _checkmin(T &t,T x) {if(t==-1) t = x; if(x < t) t = x;}
template <class T> void _checkmax(T &t,T x) {if(t==-1) t = x; if(x > t) t = x;}
typedef pair <int,int> PII;
typedef pair <double,double> PDD;
typedef long long ll;
#define foreach(it,v) for(__typeof((v).begin()) it = (v).begin(); it != (v).end ; it ++)
const int N = 1010;
double G[N][N],minCost[N],x[N],y[N],path[N][N],cost[N],ratio;
int pre[N] , vis[N] , n;
bool used[N][N];

double prim() {
    double ans = 0;
    memset(vis,0,sizeof(vis));
    memset(used,0,sizeof(used));
    memset(path,0,sizeof(path));
    vis[1] = 1;
    for(int i=1;i<=n;i++) {
        minCost[i] = G[1][i];
        pre[i] = 1;
    }
    while(1) {
        int u = -1;
        for(int i=1;i<=n;i++)
            if(!vis[i] && (u==-1 || minCost[i]<minCost[u]))
                u = i;
            if(u == -1) break;
            used[pre[u]][u] = used[u][pre[u]] = 1;
            ans += G[pre[u]][u];
            vis[u] = 1;
            for(int i=1;i<=n;i++) {
                if(vis[i] && i != u)
                    path[u][i]=path[i][u]=max(path[i][pre[u]] , minCost[u]);
                if(!vis[i]) {
                    if(minCost[i] > G[u][i]) {
                        minCost[i] = G[u][i];
                        pre[i] = u;
                    }
                }
            }
    }
    return ans;
}

int main() {
    int T;
    scanf("%d",&T);
    while(T--) {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%lf%lf%lf",x+i,y+i,cost+i);
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            G[i][j] = sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
        double ans = prim();
        ratio = -1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(i != j)
                    if(used[i][j]) checkmax(ratio,(cost[i]+cost[j])/(ans-G[i][j]));
                    else checkmax(ratio,(cost[i]+cost[j])/(ans-path[i][j]));
        printf("%.2lf\n" , ratio);
    }
    return 0;
}

 

 

 

转载于:https://www.cnblogs.com/aiiYuu/archive/2013/04/05/3001805.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值