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;
}