28 ACwing 324 贿赂FIPA 题解

贿赂FIPA

题面

本内特希望通过以赠送钻石买通国家的方式,获得更多的投票。

当然,他并不需要买通所有的国家,因为只要买通了一个大国,就等于获得了它和它统治下所有小国的投票。

例如,C 在 B 的统治下,B 在 A 的统治下,那么买通 A 就等于获得了三国的投票。

请注意,一个国家最多附庸于一个国家的统治下,附庸关系也不会构成环。

请你编写一个程序,帮助本内特求出在至少获得 m 个国家支持的情况下的最少花费是多少。

\(1 \le n \le 200, \ 0 \le m \le n\)

题解

这道题其实不难看出来是个树形背包,设 \(f(x,i)\) 表示在 \(x\) 的子树内获得 \(i\) 票所需的最小代价

然后枚举子节点转移即可,但是问题在于这道题的细节

首先,它不一定是棵树,有可能是森林,所以要建个虚拟节点,而虚拟节点是不能被算作正常的节点的,所以需要特殊处理

还有题目的特殊条件,选了一个节点,那么它的子树都会被选上,我们在枚举子树内选了多少个节点的时候,注意上界不能超,否则会访问到非法状态(根据我们一开始初始化的状态转移的)

等你把这些都想完了,发现这道题的读入也是个让人蛋疼的东西

可以用 stringstream 来简化读入,非常方便,详见 [stringstream用法](../../11 杂项/11.0 C++ 语法/stringstream 用法)

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <sstream>
#include <map>

using namespace std;

const int N = 310;

int n, m, idx;
int h[N], ne[N], ver[N], tot;
int w[N], siz[N], f[N][N], g[N];
bool in[N];
map <string, int> mp;

void add (int x, int y) {
    ver[++tot] = y;
    ne[tot] = h[x];
    h[x] = tot;
}

void dfs1 (int x) {
    if (x) siz[x] = 1;
    for (int i = h[x]; i; i = ne[i]) {
        int y = ver[i];
        dfs1 (y);
        siz[x] += siz[y];
    }
}

void dfs2 (int x) {
    if (x) f[x][siz[x]] = w[x];
    f[x][0] = 0;
    for (int i = h[x]; i; i = ne[i]) {
        int y = ver[i];
        dfs2 (y);
        // 树形背包的本质是分组背包
        // 因为下面更新的时候可能会用到 f[x][j] 这个状态,从而导致 y 子树内被选了多个,不合法
        // 所以用一个 g[] 临时存一下,防止被连续更新
        for (int j = 0; j <= siz[x]; j++) {
            g[j] = f[x][j];
        } 
        for (int j = siz[x]; j >= 0; j--) {
            for (int k = 0; k <= j && k <= siz[y]; k++) {
                g[j] = min(g[j], f[x][j - k] + f[y][k]);
            }
        }
        for (int j = 0; j <= siz[x]; j++) {
            f[x][j] = g[j]; // 更新
        }
    }
}

void solve () {
    //细节以及初始化要搞好,否则会很痛苦
    memset (f, 0x3f, sizeof f);
    memset (h, 0, sizeof h);
    memset (in, 0, sizeof in);
    memset (siz, 0, sizeof siz);
    tot = 0, idx = 0;
    mp.clear ();

    string line;
    getline (cin, line);
    for (int i = 1; i <= n; i ++) {
        getline (cin, line);
        stringstream ss (line);
        string now, son;
        ss >> now;
        if (!mp.count (now)) mp[now] = ++idx;
        ss >> w[mp[now]];
        while (ss >> son) {
            if (!mp.count (son)) mp[son] = ++idx;
            add (mp[now], mp[son]);
            in[mp[son]] = 1;
        }
    }
    for (int i = 1; i <= idx; i ++) {
        if (!in[i]) add (0, i);
    }
    dfs1 (0);
    dfs2 (0);
    int ans = 2e9;
    for (int i = m; i <= n; i ++) {
        ans = min (ans, f[0][i]);
    }
    cout << ans << endl;
}

int main () {
    ios :: sync_with_stdio (0);
    cin.tie (0);

    while (cin >> n >> m) {
        solve ();
    }

    return 0;
}
posted @ 2025-10-05 18:06  michaele  阅读(1)  评论(0)    收藏  举报