1. 程式人生 > >Genghis Khan the Conqueror 【HDU - 4126】【最優比例生成樹】

Genghis Khan the Conqueror 【HDU - 4126】【最優比例生成樹】

題目連結


  看到這道題的時候,我第一反應就是次優比例生成樹的變形,但是,思路是這樣的沒錯,卻又少許不同的地方,我們來講一下這裡的不同點,依舊是要用到pre[]字首來記錄每個節點的字首,然後判斷的是每個邊:若刪除這條邊,用其他邊進行補,會需要多少的最小花費邊。

  於是問題化簡為:我們將[L, R]這條邊的價值變為x,需要判斷的是:(一)若是這條邊是最優比例生成樹上的邊,那麼除去這條邊之後的連線兩個獨立的樹的最小代價,就是第一棵樹去匹配第二棵樹,找到最小解;(二)若是這條邊不是最優比例生成樹上的邊,那麼好辦了,直接不管就是了,加上最優比例生成樹上的權值就可以了。

  最優比例生成樹好建,怎麼判斷除去某條邊後的連線兩棵樹的最小權重?那麼就有點類似於樹形DP,不過只是思維的類似,方法上的處理會簡單好多。我們先建立這顆最優比例生成樹(這永遠是先決條件),然後,我們把樹上的節點放進鏈中,就是樹上的一個點需要包含所有與它相連的子節點,這個的目的在於處理後面的除去某邊的最短距,我這不用vector<>而是用了鏈式前向星來做一個時間上的優化,其實差不多。然後,最為重要的就是我們處理的方式,我們利用dfs(目前訪問節點,目前訪問節點的根),其中還要知道一個全域性變數就是根節點(出發節點),這樣的一個思路來想,若是從根節點出發,我們會走向兩邊,但是就是不能走到之前的根節點:光說沒用,賦上程式碼講:

int dfs(int st, int fa)
{
    int len = INF;
    for(int u=head[st]; u!=-1; u=rode[u].next)
    {
        int v=rode[u].to;
        if(v!=fa) len = min(len, dfs(v, st) );
    }
    if(st!=rt && !used[st][rt])
    {
        len = min(len, edge[st][rt]);
    }
    path[st][fa] = path[fa][st] = min(len, path[fa][st]);
    return len;
}

  其中我的len定義為旗下的訪問邊中最小的距離根節點rt的距離,之後,我們去看目前訪問節點與根節點的關係,若不是根節點並且不與根節點構成生成樹上的邊,我們就去優化len的大小,然後最後更新下path[目前訪問節點][其的根](path指的就是去這邊以後其他邊等效該邊的最優解)的距離。


#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 INF 0x3f3f3f3f
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN=3005;
int N, M, Q, edge[maxN][maxN], path[maxN][maxN], pre[maxN], head[maxN*maxN], cnt, rt;
bool used[maxN][maxN];
struct Eddge
{
    int next, to, val;
    Eddge(int a=-1, int b=0, int c=0):next(a), to(b), val(c) {}
}rode[maxN*maxN];
void addEddge(int u, int v, int val)
{
    rode[cnt] = Eddge(head[u], v, val);
    head[u] = cnt++;
}
void init()
{
    cnt=0;
    for(int i=0; i<N; i++)
    {
        for(int j=i+1; j<N; j++)
        {
            edge[i][j] = edge[j][i] = path[i][j] = path[j][i] = INF;
            used[i][j] = used[j][i] = false;
        }
    }
    memset(head, -1, sizeof(head));
}
ll prime(int pos)
{
    ll ans=0;
    bool vis[maxN]; memset(vis, false, sizeof(vis));
    int lowercost[maxN];
    for(int i=0; i<N; i++) if(i!=pos){ lowercost[i]=edge[pos][i]; pre[i]=pos; }
    vis[pos]=true;  pre[pos]=-1;
    for(int line=1; line<N; line++)
    {
        int minn=INF;
        for(int i=0; i<N; i++)
        {
            if(!vis[i] && minn>lowercost[i])
            {
                minn=lowercost[i];
                pos=i;
            }
        }
        vis[pos]=true;
        addEddge(pos, pre[pos], minn);
        addEddge(pre[pos], pos, minn);
        used[pos][pre[pos]] = used[pre[pos]][pos] = true;
        ans+=minn;
        for(int i=0; i<N; i++)
        {
            if(!vis[i] && lowercost[i]>edge[pos][i]) { lowercost[i]=edge[pos][i]; pre[i]=pos; }
        }
    }
    return ans;
}
int dfs(int st, int fa)
{
    int len = INF;
    for(int u=head[st]; u!=-1; u=rode[u].next)
    {
        int v=rode[u].to;
        if(v!=fa) len = min(len, dfs(v, st) );
    }
    if(st!=rt && !used[st][rt])
    {
        len = min(len, edge[st][rt]);
    }
    path[st][fa] = path[fa][st] = min(len, path[fa][st]);
    return len;
}
void solve()
{
    for(int i=0; i<N; i++)
    {
        rt = i;
        dfs(i, i);
    }
}
int main()
{
    while(scanf("%d%d", &N, &M) && (N | M))
    {
        init();
        for(int i=1; i<=M; i++)
        {
            int e1, e2, e3;
            scanf("%d%d%d", &e1, &e2, &e3);
            edge[e1][e2] = edge[e2][e1] = e3;
        }
        ll Minn_Tree=prime(0), ans=0;
        solve();
        scanf("%d", &Q);
        for(int i=1; i<=Q; i++)
        {
            int e1, e2, e3;
            scanf("%d%d%d", &e1, &e2, &e3);
            if(used[e1][e2]) ans += Minn_Tree - edge[e1][e2] + min(e3, path[e1][e2]);
            else ans += Minn_Tree;
        }
        printf("%.4lf\n", (double)ans/(double)Q );
    }
    return 0;
}