HDU 4081 Qin Shi Huang's National Road System

传送门

这个题也是类似求比值最优的问题,应该不能简单贪心求解,就只好枚举喽。
A是某条边上两点的点权之和,B是在上述两点已经连通的情况下图的最小生成树的值。想让A/B最大。
枚举每条边,也就枚举了每种可能的A,有两种情况:
- 该边 是 mst上的边,在这两点已经确定的情况下,B的最小值就是mst的值减去该边的权值。(借助kruskal算法可以理解)
- 该边 不是 mst上的边,此时mst加上该边一定会形成有且只有一个环,环上除此边以外任意一条边的权值只会小于等于该边的权值,而且环上去除任意一条边之后就是生成树。所以在这种情况下,B的最小值就是mst的值减去该环上属于mst的边(除此边之外)中的最大权值。
可以设这条不在mst中的边的起点和终点为i,j,那么只要求出在mst中ij路径上(有且只有一条路径)的最大边权max_edge[i][j]就可以了。用prim顺便求这个会比较方便。(在松弛循环中可以根据前驱来更新,且prim要确定入选mst边时也会根据前驱)
求“次小生成树”也会用到max_edge[i][j]

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cmath>
using namespace std;

const int INF = 1e9;
const int MAXN = 1e3 + 2;
int N;

struct Point
{
    int x, y, pop;
}city[MAXN];

double g[MAXN][MAXN];
bool active[MAXN][MAXN];
double max_edge[MAXN][MAXN];
bool vis[MAXN];
double d[MAXN];
int pre[MAXN];
double ans;
double B;

void init()
{
    fill(vis, vis + N, 0);
    fill(d, d + N, INF);
    memset(active, 0, sizeof active);
    ans = -1;
}

double getDistance(int i, int j)
{
    return sqrt((double)(pow(city[i].x - city[j].x, 2) + pow(city[i].y - city[j].y, 2)));
}

void prim()
{
    B = 0;
    d[0] = 0;
    for (int i = 0; i < N; i++)
    {
        double mind = INF;
        int u = -1;
        for (int j = 0; j < N; j++)
        {
            if (!vis[j] && d[j] < mind)
            {
                mind = d[j];
                u = j;
            }
        }
        B += mind;
        vis[u] = 1;
        if (u) active[u][pre[u]] = active[pre[u]][u] = 1;
        for (int j = 0; j < N; j++)
        {
            if (vis[j] && j != u)       ////
            {
                if (pre[u] == j) max_edge[u][j] = max_edge[j][u] = g[u][j];
                else max_edge[u][j] = max_edge[j][u] = max(max_edge[pre[u]][j], g[u][pre[u]]);
            }
            else if (!vis[j] && g[u][j] < d[j])
            {
                d[j] = g[u][j];
                pre[j] = u;
            }
        }
    }
}

int main()
{
    int T;
    scanf("%d", &T);
    for (; T--;)
    {
        scanf("%d", &N);
        init();
        for (int i = 0; i < N; i++)
            scanf("%d%d%d", &city[i].x, &city[i].y, &city[i].pop);
        for (int i = 0; i < N; i++)
            for (int j = 0; j < N; j++)
                if (i != j) g[i][j] = g[j][i] = getDistance(i, j);
        prim();   // 其重要任务是计算数组max_edge[][]
        for (int i = 0; i < N; i++)
        {
            for (int j = 0; j < N; j++)
            {
                if (i != j)
                {
                    if (active[i][j])
                        ans = max(ans, ((double)(city[i].pop + city[j].pop)) / (B - g[i][j]));
                    else ans = max(ans, ((double)(city[i].pop + city[j].pop)) / (B - max_edge[i][j]));
                }
            }
        }
        printf("%.2f\n", ans);
    }

    return 0;
}
posted @ 2017-04-10 23:28  CrossingOver  阅读(67)  评论(0编辑  收藏  举报