HDU 6769 In Search of Gold 二分答案+树形DP

Posted kaka0010

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 6769 In Search of Gold 二分答案+树形DP相关的知识,希望对你有一定的参考价值。

原题链接:https://acm.hdu.edu.cn/showproblem.php?pid=6769

题意

有一棵树,每条边都有两个权值a,b可以选择,一共可以选择k条a权值,n-1-k条b权值,问最小的树的直径是多少?

分析

非常好的一道题,可以让人基本上弄懂树形背包的复杂度。

先考虑状态,根据套路容易写出 f [ u ] [ j ] f[u][j] f[u][j]代表u节点选择j条a权值的边到最远叶子结点距离的最小值,这样状态转移就可以轻松推出

f [ u ] [ i + j + 1 ] = m i n ( f [ u ] [ i + j + 1 ] , f [ u ] [ i ] , f [ v ] [ j ] + a ) f[u][i+j+1]=min(f[u][i+j+1], f[u][i], f[v][j]+a) f[u][i+j+1]=min(f[u][i+j+1],f[u][i],f[v][j]+a)

f [ u ] [ i + j ] = m i n ( f [ u ] [ i + j ] , f [ u ] [ i ] , f [ v ] [ j ] + b ) f[u][i+j]=min(f[u][i+j], f[u][i], f[v][j]+b) f[u][i+j]=min(f[u][i+j],f[u][i],f[v][j]+b)

此时还是存一个 t m p [ j ] tmp[j] tmp[j]来暂时表示当前层的转移,这样不会造成dp反复更新,而且复杂度也是NK的。

接着我们怎么和直径扯上关系呢,如果想直接靠这个转移得到最小直径是不行的,状态还不够表示,因此考虑二分答案,我们直接在合并时判断当前最长链是否超过mid,如果超过则不合并答案,最后检查根节点的最长链长度是否超过mid,答案是满足单调性的。

总时间复杂度也就是 O ( N K l o g A n s ) O(NKlogAns) O(NKlogAns)

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int ul;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const ll mod = 1e9 + 7;
const double eps = 1e-8;

#define lowbit(i) (i & -i)
#define Debug(x) cout << (x) << endl
#define fi first
#define se second
#define mem memset
#define endl '\\n'

namespace StandardIO {
    template<typename T>
    inline void read(T &x) {
        x = 0; T f = 1;
        char c = getchar();
        for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
        for (; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - '0';
        x *= f;
    }

    template<typename T>
    inline void write(T x) {
        if (x < 0) putchar('-'), x *= -1;
        if (x >= 10) write(x / 10);
        putchar(x % 10 + '0');
    }
}

using namespace StandardIO;
int n, k, h[N], cnt;
struct Edge {
    int to, next, a, b;
}e[N];
void add(int u, int v, int a, int b) {
    e[cnt].to = v;
    e[cnt].a = a;
    e[cnt].b = b;
    e[cnt].next = h[u];
    h[u] = cnt++;
}
ll f[N][25], tmp[25];
int siz[N];
void dfs(int x, int fa, ll len) {
    siz[x] = 0;
    for (int i = 0; i <= k; i++) f[x][i] = 0;
    for (int i = h[x]; ~i; i = e[i].next) {
        int v = e[i].to;
        if (v == fa) continue;
        dfs(v, x, len);
        int K = min(siz[x] + siz[v] + 1, k);
        for (int j = 0; j <= K; j++) tmp[j] = len + 1;
        for (int j = 0; j <= min(siz[x], K); j++) {
            for (int l = 0; l <= siz[v] && j + l <= K; l++) {
                if (f[x][j] + f[v][l] + e[i].a <= len) {
                    tmp[j + l + 1] = min(tmp[j + l + 1], max(f[x][j], f[v][l] + e[i].a));
                }
                if (f[x][j] + f[v][l] + e[i].b <= len) {
                    tmp[j + l] = min(tmp[j + l], max(f[x][j], f[v][l] + e[i].b));
                }
            }
        }
        for (int j = 0; j <= K; j++) f[x][j] = tmp[j];
        siz[x] += siz[v] + 1;
    }
}
inline void solve() {
    int T; read(T); while (T--) {
        read(n), read(k);
        for (int i = 0; i <= n; i++) h[i] = -1;
        cnt = 0;
        ll r = 0, l = 1;
        for (int i = 1; i <= n - 1; i++) {
            int u, v, a, b;
            read(u), read(v), read(a), read(b);
            add(u, v, a, b);
            add(v, u, a, b);
            r += max(a, b);
        }
        while (l <= r) {
            ll mid = (l + r) >> 1;
            dfs(1, 0, mid);
            if (f[1][k] <= mid) r = mid - 1;
            else l = mid + 1;
        }
        printf("%lld\\n", l);
    }
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}

以上是关于HDU 6769 In Search of Gold 二分答案+树形DP的主要内容,如果未能解决你的问题,请参考以下文章

Application of Breath-first search in AI(route search)

Application of Breath-first search in AI(route search)

LeetCode 702. Search in a Sorted Array of Unknown Size

codeforces A. In Search of an Easy Problem

[HDU 2966]In case of failure

Panda Games: Corporate Disclosure in the Eclipse of Search