Hdu 4081 Qin Shi Huang's National Road System

 

大意:

有n个城市,秦始皇要修用n-1条路把它们连起来,要求从任一点出发,都可以到达其它的任意点。秦始皇希望这所有n-1条路长度之和最短。然后徐福突然有冒出来,说是他有魔法,可以不用人力、财力就变出其中任意一条路出来。

秦始皇希望徐福能把要修的n-1条路中最长的那条变出来,但是徐福希望能把要求的人力数量最多的那条变出来。对于每条路所需要的人力,A是指这条路连接的两个城市的人数之和,对,是两个城市。

最终,秦始皇给出了一个公式,A/B,A是指要徐福用魔法变出的那条路所需人力, B是指除了徐福变出来的那条之外的所有n-2条路径长度之和,选使得A/B值最大的那条。

 

思路:这道题可以看做是次小生成树的变形,我们可以通过枚举的方法求A/B的最大值。

 

为了使的A/B值最大,首先是需要是B尽量要小,所以可先求出n个城市的最小生成树。然后,就是决定要选择那一条用徐福的魔法来变。

1、枚举的边属于MST。如果这一条边已经是属于最小生成树上的,那么最终式子的值是A/(cnt-w[i][j])。

2、如果这一条不属于MST, 那么添加上这条边,就会有n条边,那么就会使得有了一个环,为了使得它还是一个生成树,就要删掉环上的一棵树。 为了让生成树尽量少,那么就要删掉除了加入的那条边以外,权值最大的那条路径。 假设删除的那个边的权值是Max[i][j],那么就是A/(cnt-Max[i][j]).

解这题的关键也在于怎样求出次小生成树,关于次小生成树,可以去网上搜搜资料,

POJ 1679 是不错的入门题,我的模板链接:http://www.cnblogs.com/g0feng/archive/2012/10/23/2735720.html

 

CODE:

 

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <cmath>
using  namespace std;

#define MAXN 1100
#define INF 0X3F3F3F3F

double w[MAXN][MAXN], d[MAXN];
int use[MAXN][MAXN];
double Max[MAXN][MAXN];
int fa[MAXN], vis[MAXN], cost[MAXN];

int n, m;
double cnt;

struct node
{
     double x, y;
}a[MAXN];

double dis( const node &a,  const node &b)
{
     return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

void init()
{
    cnt =  0;
    memset(use,  0sizeof(use));
    memset(w, INF,  sizeof(w));
    memset(vis,  0sizeof(vis));
}

void Prim( int src)
{
     for( int i =  1; i <= n; i++) d[i] = (i == src)?  0:INF;
     for( int i =  1; i <= n; i++) fa[i] = i;
     for( int i =  1; i <= n; i++)
    {
         int x;
         double m = INF;
         for( int y =  1; y <= n; y++)  if(!vis[y] && d[y] < m) m = d[x=y];
         for( int y =  1; y <= n; y++)  if(vis[y]) /* 这是算法的关键,记录下vis[y]到k的路径中权值最大的值,用于替换处理 */
        {
            Max[y][x] = Max[x][y] = max(d[x], Max[y][fa[x]]);    
        }
        vis[x] =  1;
        use[x][fa[x]] = use[fa[x]][x] =  1// 标记在MST树中
        cnt += m;
         for( int y =  1; y <= n; y++)
        {
             if(!vis[y] && d[y] > w[x][y])
            {
                d[y] = w[x][y];
                fa[y] = x;  // 记录父亲节点 
             }
        }
    }
}

int main()
{
     int T;
    scanf( " %d ", &T);
     while(T--)
    {
        init();
        scanf( " %d ", &n);
         for( int i =  1; i <= n; i++)
        {
            scanf( " %lf%lf%d ", &a[i].x, &a[i].y, &cost[i]);
        }
         for( int i =  1; i <= n; i++)
        {
             for( int j =  1; j <= n; j++)  if(i != j)
            {
                w[i][j] = w[j][i] = dis(a[i], a[j]);
            }
        }
        Prim( 1);
         double ans = - 1;
         for( int i =  1; i <= n; i++)
        {
             for( int j =  1; j <= n; j++)  if(i != j)
            {
                 if(use[i][j])
                {
                    ans = max(ans, (cost[i]+cost[j])/(cnt-w[i][j]));
                }
                 else
                {
                    ans = max(ans, (cost[i]+cost[j])/(cnt-Max[i][j]));
                }
            }
        }
        printf( " %.2lf\n ", ans);
    }
     return  0;
}

 

 

你可能感兴趣的:(System)