題意
給n個節點的樹,邊權
題解
令
即要求是否存在:
即要使:
注意到:
即枚舉圖,確定
考慮到樹的最大權匹配能
對每棵子樹,計算
接下來這題最噁心的地方在於如何合併,即如何保證,或者說按照什麼順序合併能夠使得保證合併得到的值,在情況合法(滿足整體最大權匹配)的前提下的最小目標函數值。
(這段是錯誤解法,不希望被誤導的讀者請自行跳過。。。。)
首先想到的想法是對每個子樹只記錄三個值:
接下來是合併順序的問題。
按照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
正確的做法是:對每個
那麼觀察
故,我們把所有的
注意到這題只要找出是否存在
在省狀態之前要考慮清楚狀態之間的聯繫,在計算目標的時候實質上是要保證兩點:
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;
}