1. 程式人生 > >Qin Shi Huang's National Road System 【HDU - 4081】【次優比例生成樹(最優比例生成樹變形)】

Qin Shi Huang's National Road System 【HDU - 4081】【次優比例生成樹(最優比例生成樹變形)】

題目連結


  題目問的是有N個點集,問你建立N-1條邊的情況下,使得路徑最短的方案數,但是呢,題目中又給出徐福會魔法可以把一條路徑變成免費道路,但是有個限制就是希望這條路上兩個城市的人口數比上除去該路徑以外整棵樹的權值能最大。

  一開始就想到的是最優比例生成樹,但是卻有很多地方不大對,沒法往下推下去,強行過了樣例交了兩發最後草草WA了,後來畫了張圖去推了下,發現了件事,我們看到樣例一所給出的結論,樣例一的圖我們先按最優比例生成樹來寫,然後我們去遍歷O(N^2)的邊集,去尋最優比例生成樹上的邊上的兩端點的人數和比上除去這條邊以外的整棵樹的權重,發現答案是60.00,而非65.00,造成這樣的原因就在於我們需要再建的魔法邊不是人數“20”->“100”的那條邊,而是“30”->“100”的那條邊那麼意味著我們還需要考慮這樣的可能,那麼需要怎麼做?

  我們在建樹的時候還需要考慮成環的可能了。就是說假如兩條邊不是樹上的邊,但是有可能這兩點建立的邊會成為最後的答案邊,就如同樣例一,所以,我們考慮加上這條邊之後成環後除該邊以外環內的其他邊中路徑最長的,因為我們只需要用樹的權重剪去這條最長邊的路徑也一樣是樹。

  就是怎樣處理環是個題目,這裡的思想有點類似dp但又有少許區別,就是我們遇到前面已經放進prime樹中的點,說明這兩點肯定就不能構成邊,而且會使得之前的邊在一起構成環,所以,怎麼解?我們要去找到這兩點之間的最長邊的權值,可以通過找之前狀態的以及前一條邊的大小,因為目前節點pos的pre[pos]就是指的前一個狀態下

的,而前一刻狀態唯獨不包括的就是目前存進prime中的邊,所以比較下哪個大,就可以往後走了。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN=1005;
int N;
struct node
{
    int x, y, person;
    node(int a=0, int b=0, int c=0):x(a), y(b), person(c) {}
}a[maxN];
double Dist(node e1, node e2) { return 1.*sqrt( 1.*(e1.x-e2.x)*(e1.x-e2.x) + 1.*(e1.y-e2.y)*(e1.y-e2.y) ); }
double edge[maxN][maxN], path[maxN][maxN], lowercost[maxN];
bool used[maxN][maxN];
double Prime(int pos)
{
    memset(path, 0, sizeof(path));
    memset(used, false, sizeof(used));
    double ans=0;
    bool vis[maxN]; memset(vis, false, sizeof(vis));
    int pre[maxN];
    pre[pos]=0;
    for(int i=1; i<=N; i++) if(i!=pos) { lowercost[i]=edge[pos][i]; pre[i]=pos; }
    vis[pos]=true;
    for(int line=1; line<N; line++)
    {
        double minn = INF;
        for(int i=1; i<=N; i++)
        {
            if(!vis[i] && minn>lowercost[i]) { minn=lowercost[i]; pos=i; }
        }
        vis[pos]=true;
        ans+=minn;
        used[pos][pre[pos]] = used[pre[pos]][pos] = true;
        for(int i=1; i<=N; i++)
        {
            if(vis[i] && i!=pos) path[i][pos] = path[pos][i] = max(minn, path[i][pre[pos]]);
            if(!vis[i] && lowercost[i]>edge[pos][i])
            {
                lowercost[i] = edge[pos][i];
                pre[i] = pos;
            }
        }
    }
    return ans;
}
int main()
{
    int T;  scanf("%d", &T);
    while(T--)
    {
        scanf("%d", &N);
        for(int i=1; i<=N; i++) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].person);
        for(int i=1; i<=N; i++) for(int j=i+1; j<=N; j++) edge[i][j] = edge[j][i] = Dist(a[i], a[j]);
        double minn_Tree=Prime(1), ans=0;
        for(int i=1; i<=N; i++)
        {
            for(int j=i+1; j<=N; j++)
            {
                if(used[i][j]) ans=max(ans, 1.*(a[i].person+a[j].person)/(minn_Tree-edge[i][j]));
                else ans=max(ans, 1.*(a[i].person+a[j].person)/(minn_Tree-path[i][j]));
            }
        }
        printf("%.2lf\n", ans);
    }
    return 0;
}