HDU 5561 【2015合肥現場賽】 Kingdom of Tree

題意

給n個節點的樹,邊權L[x][y] ,定義一條邊xyS 當且僅當R[x]+R[y]L[x][y] ,現在要給每個節點x確定R[x]0 ,使得 R[x]L[x][y][xyS] 最小。
n<=30,0<=L[x][y]<=1e9

題解

R[x]L[x][y][xyS]λ
即要求是否存在:
R[x](λL[x][y])[xyS]0
即要使:
min{R[x](λL[x][y])[xyS]}0

注意到:
R[x]+R[y]L[x][y] 前提下,R[x] 實質是最小頂標和,而樹是二分圖,最小頂標和轉化爲最大權匹配。

即枚舉圖,確定[xyS] 的邊,在新圖中做最大權匹配,得到minR[x]

考慮到樹的最大權匹配能O(n) 求得,直接做複雜度是2n×n 不能過。解決方法是利用樹的性質,找出質心,分隔樹,易知分隔得到的子樹大小n/2 ,可行。

對每棵子樹,計算f[u][0]f[u][1] 代表u 這個點是否被匹配情況下,u這棵子樹的最大權匹配。

接下來這題最噁心的地方在於如何合併,即如何保證,或者說按照什麼順序合併能夠使得保證合併得到的值,在情況合法(滿足整體最大權匹配)的前提下的最小目標函數值。


(這段是錯誤解法,不希望被誤導的讀者請自行跳過。。。。)

首先想到的想法是對每個子樹只記錄三個值:dp0 表示在所有情況下,在子樹中得到的最大權匹配,dp10 表示子樹根與總根相連這條邊加入後,整棵子樹的最大權匹配,且總根不被匹配上,dp11 表示子樹根與總根相連這條邊加入後,整棵子樹的最大權匹配,且總根被匹配上。

接下來是合併順序的問題。
按照w排序直接做?錯,比如說
A B 3
B C 2
A D 2
每個子樹選的時候,A-B是最大匹配邊,而當兩個連起來的時候,B-C A-D成了最大匹配邊

那麼按照dp11-dp0排序?也是錯的。
同樣是上面的例子,這時候B子樹如果不匹配A-B這條邊,子樹中最優情況是不找匹配邊,而此時A-D連起來時,A-B反而又成了匹配邊。。。
這個問題引出:如果不記錄子樹內選邊情況,單記錄是否與跟這條邊相連是不夠的。。。

QwQ


正確的做法是:對每個root 連出去的點u ,計算hu,S 表示u 這棵子樹,選邊情況爲S 的情況下的最大權匹配,gu,S 表示u 這棵子樹,選邊情況爲S 的情況下,再加入rot ->u 這條邊的情況下的最大權匹配“增量”,fu,S=hu,Sval(S) 表示所求的目標函數對應在這棵樹上的值。

那麼觀察root ,假設它要用某個u ,在其選邊情況爲S 的情況下,與其匹配,那麼對其它子樹v都必須滿足gv,S<=gu,S ,在這個前提下,求目標函數,即爲

fu,S+gu,S+v(minS{fv,Sval(rot>v)|vugv,S<=gu,S},minS{fv,S|vu})

故,我們把所有的gu,S 排序,從小到大依次計算目標函數,
注意到這題只要找出是否存在0 的方案,所以若存在目標函數0 的情況則返回True ,否則False

在省狀態之前要考慮清楚狀態之間的聯繫,在計算目標的時候實質上是要保證兩點:
1. 儘可能保證縮減狀態,通常利用最優性質(即類似最優性剪枝),使目標狀態分割爲多個子狀態的最優值,再合併
2. 要保證不能引入非法狀態。

(做了一天終於搞定了QwQ)

code

#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <deque>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
using namespace std;

typedef long double LD;
typedef long long LL;

const int maxn = 40;

const LL INF = 0x3f3f3f3f3f3f3f3fLL;
const LD eps = 1e-15;

int n, rt;

int tot, st[maxn];
int lk[maxn << 1], b[maxn << 1], c[maxn << 1];
bool fg[maxn << 1];
void addedge(int u, int v, int w) {
    lk[++ tot] = st[u]; b[tot] = v; c[tot] = w; fg[tot] = 0; st[u] = tot;
}

struct Node {
    int u, S;
    LD f; LL g, w;
    bool operator <(const Node& B) const {
        return g < B.g;
    }
} A[maxn * (1<<16)]; int AN;

int e[maxn], eN;

void dfsA1(int u, int fa) {
    for (int i = st[u]; i; i = lk[i]) {
        int v = b[i];
        if (v == fa) continue;
        e[eN ++] = i;
        dfsA1(v, u);
    }
}

LL f[maxn][2];
void dfsA2(int u, int fa) {
    f[u][0] = 0, f[u][1] = -INF;
    for (int i = st[u]; i; i = lk[i]) {
        int v = b[i];
        if (v == fa) continue;
        dfsA2(v, u);

        LL f0 = -INF, f1 = -INF;
        f0 = f[u][0] + max(f[v][0], f[v][1]);
        f1 = f[u][1] + max(f[v][0], f[v][1]);
        if (fg[i]) f1 = max(f1, f[u][0] + c[i] + f[v][0]);
        f[u][0] = max(f[u][0], f0), f[u][1] = max(f[u][1], f1);
    }
}

LD mu[maxn];

bool calc(LD lam) {
    AN = 0;

    for (int i = 1; i <= n; ++ i) mu[i] = 0;
    for (int i = st[rt]; i; i = lk[i]) {
        int u = b[i], w = c[i];

        eN = 0;
        dfsA1(u, rt);
        for (int S = 0; S < (1<<eN); ++ S) {
            LL sEw = 0;
            for (int i = 0; i < eN; ++ i) if (S&(1<<i)) { fg[e[i]] = fg[e[i]^1] = 1; sEw += c[e[i]]; }
            dfsA2(u, rt);
            for (int i = 0; i < eN; ++ i) if (S&(1<<i)) fg[e[i]] = fg[e[i]^1] = 0;

            LL hu = max(f[u][0], f[u][1]);
            LD fu = hu - lam * sEw;
            LL gu = max(f[u][0] + w, f[u][1]) - hu;
            A[AN ++] = (Node){u, S, fu, gu, w};

            if (S > 0 && fu < eps)
                return 1;
        }
    }

    sort(A, A+AN);

    for (int i = 0; i < AN; ++ i) {
        int u = A[i].u; LD fu = A[i].f; LL gu = A[i].g;

        if (A[i].S == 19) {
            int pp = 0;
        }

        LD s = 0;
        for (int j = 1; j <= n; ++ j) if (j != u) s += mu[j];

        if (fu + gu - A[i].w * lam + s < eps) return 1;

        mu[u] = min(mu[u], fu - A[i].w * lam);
    }

    return 0;
}

int cen, vcen;
int sz[maxn], mxChSz[maxn];
void dfsC(int u, int fa, int n) {
    sz[u] = 1; mxChSz[u] = 0;
    for (int i = st[u]; i; i = lk[i]) {
        int v = b[i];
        if (v == fa) continue;
        dfsC(v, u, n);
        sz[u] += sz[v];
        mxChSz[u] = max(mxChSz[u], sz[v]);
    }
    mxChSz[u] = max(n - sz[u], mxChSz[u]);
    if (mxChSz[u] < vcen) {
        cen = u; vcen = mxChSz[u];
    }
}
int getCen() {
    vcen = n+1;
    dfsC(1, 0, n);
    return cen;
}

void solve() {
    scanf("%d", &n);

    tot = 1; memset(st, 0, sizeof st);
    for (int i = 1; i < n; ++ i) {
        int u, v, w; scanf("%d%d%d", &u, &v, &w);
        addedge(u, v, w); addedge(v, u, w);
//      printf("%d %d %d\n", u, v, w);
    }

    rt = getCen();

    LD L = 0, R = 1 + eps;
    for (int z = 20; z --; ) {
        LD M = (L + R) / 2;
        if (calc(M)) R = M; else L = M;
    }
    printf("%.8lf\n", (double)R);
}

int main() {
//  freopen("J.in", "r", stdin);
//  freopen("J.out", "w", stdout);

    int kase, i = 0; scanf("%d", &kase);
    for (int i = 1; i <= kase; ++ i) {
        printf("Case #%d: ", i);
        solve();
    }
//  for(;;);
    return 0;
}
發佈了56 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章