HDU 4081 Qin Shi Huang's National Road System

题目大意:

        现有t个测例(t ≤ 10),每个测例都会给出n个点的坐标(2 < n ≤ 1,000),每个点的坐标信息形式诸如"X Y P",表示点的坐标为(X, Y),点权值为P,都为整型,其中0 ≤ X, Y ≤ 1,000,0 < P < 100,000,现要求n - 1条边将所有n个点都连通,并且将其中一条边的权值变为0,现要使变为0的边的两点的权值之和比上其它边权值之和最大,该如何构造这n - 1条以及如何挑选变为0的边才能是该比值最大,要求对于每个测例都输出该最大值,保留两位小数(边长即为两点的欧几里得距离)。

题目链接

注释代码:

/*                                  
 * Problem ID : HDU 4081 Qin Shi Huang's National Road System
 * Author     : Lirx.t.Una                                  
 * Language   : C++                      
 * Run Time   : 171 ms                                  
 * Run Memory : 18200 KB                                  
*/

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>

//思路:
//先求MST使得n个点连通且边数为n - 1,并且使得总边权尽可能小
//这也使得点权比边权尽可能大
//然后在挑选边变为0测试ratio的大小,但是最小生成树可能有多个
//本来边(i, j)不在当前MST中,但是有可能在另一个MST中
//因此需要通过max_seg[i][j]换边得到另一种最小生成树
//但是g[i][j]并不一定等于max_seg[i][j],但是没有关系
//因为即使替换了也要将其边权变为0,就相当于将两点合为一点,因此没有关系
//因此最后逐个检查图中的每条边g[i][j],如果g[i][j]是当前MST中的点则非0边权为总边权减去g[i][j]
//否则非0边权就是总边权减去max_seg[i][j]

//最大点数
#define    MAXN    1000

using namespace std;

struct    Point {//每个点的坐标以及点权值

    int        x, y;
    int        w;

    friend istream &
    operator>>(istream &is, Point &p) {
    
        is >> p.x >> p.y >> p.w;
        return is;
    }

    int
    POW(int a) { return a * a; }

    double
    operator^(Point &oth) {
    
        return sqrt((double)( POW( x - oth.x ) + POW( y - oth.y ) ));
    }
};

struct    Node {//Prim堆优化中的结点

    int       u;
    double    d;

    Node(void) {}

    Node( int uu, double dd ) : u(uu), d(dd) {}

    bool
    operator<(const Node &oth)
    const {
    
        return d > oth.d;
    }
};

double        max_seg[MAXN + 1][MAXN + 1];
double        g[MAXN + 1][MAXN + 1];
double        d[MAXN + 1];

Point    p[MAXN + 1];//表示每个点

int        pre[MAXN + 1];

bool    vis[MAXN + 1];
bool    mst[MAXN + 1][MAXN + 1];

inline double
max( double a, double b ) {

    return a > b ? a : b;
}

double//返回最小生成树的总边权
prim(int n) {

    int        i;
    int        u, v;
    int        nv;

    double    ans;

    Node    node;

    priority_queue<Node>    heap;

    memset(vis, 0, sizeof(vis));
    memset(mst, 0, sizeof(mst));
    memset(max_seg, -1, sizeof(max_seg));

    vis[1] = true;
    for ( i = 2; i <= n; i++ ) {
    
        d[i]   = g[1][i];
        pre[i] = 1;

        heap.push(Node( i, d[i] ));
    }

    ans = 0.0;
    nv  = 1;
    while (true) {//完全图必定有解,无需判断
    
        while (true) {//必定有解
        
            node = heap.top();
            heap.pop();

            if ( !vis[ u = node.u ] ) {
            
                nv++;
                ans += node.d;

                mst[ pre[u] ][u] = true;
                mst[u][ pre[u] ] = true;

                for ( i = 1; i <= n; i++ )
                    if ( vis[i] ) {
                    
                        max_seg[i][u] = max( max_seg[i][ pre[u] ], node.d );
                        max_seg[u][i] = max_seg[i][u];
                    }

                vis[u] = true;

                break;
            }
        }

        if ( nv == n ) break;

        for ( v = 2; v <= n; v++ )
            if ( !vis[v] && g[u][v] < d[v] ) {
            
                d[v]   = g[u][v];
                pre[v] = u;

                heap.push(Node( v, d[v] ));
            }
    }

    return ans;
}

int
main() {

    int        t;
    int        n;

    int        i, j;

    double    B;//MST边的总权值
    double    r;//最后的比例

    scanf("%d", &t);
    while ( t-- ) {
    
        scanf("%d", &n);

        for ( i = 1; i <= n; i++ ) cin >> p[i];
        for ( i = 1; i <= n; i++ )
            for ( j = i + 1; j <= n; j++ ) {
            
                g[i][j] = p[i] ^ p[j];
                g[j][i] = g[i][j];
            }

        r = -1.0;//初始化为一个极小值
        B = prim(n);

        for ( i = 1; i <= n; i++ )
            for ( j = i + 1; j <= n; j++ )
                if ( mst[i][j] )//MST中的边之间边为0试一下
                    r = max( r, ( p[i].w + p[j].w ) / ( B - g[i][j] ) );
                else//非MST中的边g[i][j]替换max_seg[i][j]再变为0试一下
                    r = max( r, ( p[i].w + p[j].w ) / ( B - max_seg[i][j] ) );

        printf("%.2lf\n", r);
    }        
	
    return 0;
}
无注释代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>

#define    MAXN    1000

using namespace std;

struct    Point {

    int        x, y;
    int        w;

    friend istream &
    operator>>(istream &is, Point &p) {
    
        is >> p.x >> p.y >> p.w;
        return is;
    }

    int
    POW(int a) { return a * a; }

    double
    operator^(Point &oth) {
    
        return sqrt((double)( POW( x - oth.x ) + POW( y - oth.y ) ));
    }
};

struct    Node {

    int        u;
    double    d;

    Node(void) {}

    Node( int uu, double dd ) : u(uu), d(dd) {}

    bool
    operator<(const Node &oth)
    const {
    
        return d > oth.d;
    }
};

double        max_seg[MAXN + 1][MAXN + 1];
double        g[MAXN + 1][MAXN + 1];
double        d[MAXN + 1];

Point    p[MAXN + 1];

int        pre[MAXN + 1];

bool    vis[MAXN + 1];
bool    mst[MAXN + 1][MAXN + 1];

inline double
max( double a, double b ) {

    return a > b ? a : b;
}

double
prim(int n) {

    int        i;
    int        u, v;
    int        nv;

    double    ans;

    Node    node;

    priority_queue<Node>    heap;

    memset(vis, 0, sizeof(vis));
    memset(mst, 0, sizeof(mst));
    memset(max_seg, -1, sizeof(max_seg));

    vis[1] = true;
    for ( i = 2; i <= n; i++ ) {
    
        d[i]   = g[1][i];
        pre[i] = 1;

        heap.push(Node( i, d[i] ));
    }

    ans = 0.0;
    nv    = 1;
    while (true) {
    
        while (true) {
        
            node = heap.top();
            heap.pop();

            if ( !vis[ u = node.u ] ) {
            
                nv++;
                ans += node.d;

                mst[ pre[u] ][u] = true;
                mst[u][ pre[u] ] = true;

                for ( i = 1; i <= n; i++ )
                    if ( vis[i] ) {
                    
                        max_seg[i][u] = max( max_seg[i][ pre[u] ], node.d );
                        max_seg[u][i] = max_seg[i][u];
                    }

                vis[u] = true;

                break;
            }
        }

        if ( nv == n ) break;

        for ( v = 2; v <= n; v++ )
            if ( !vis[v] && g[u][v] < d[v] ) {
            
                d[v]   = g[u][v];
                pre[v] = u;

                heap.push(Node( v, d[v] ));
            }
    }

    return ans;
}

int
main() {

    int        t;
    int        n;

    int        i, j;

    double    B;
    double    r;

    scanf("%d", &t);
    while ( t-- ) {
    
        scanf("%d", &n);

        for ( i = 1; i <= n; i++ ) cin >> p[i];
        for ( i = 1; i <= n; i++ )
            for ( j = i + 1; j <= n; j++ ) {
            
                g[i][j] = p[i] ^ p[j];
                g[j][i] = g[i][j];
            }

        r = -1.0;
        B = prim(n);

        for ( i = 1; i <= n; i++ )
            for ( j = i + 1; j <= n; j++ )
                if ( mst[i][j] ) 
                    r = max( r, ( p[i].w + p[j].w ) / ( B - g[i][j] ) );
                else
                    r = max( r, ( p[i].w + p[j].w ) / ( B - max_seg[i][j] ) );

        printf("%.2lf\n", r);
    }        

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值