描述
有
n
个农民, 他们住在
约定: n≤2000 , X≤104 ,一定有解。
Sol
(这道题不像它的通过人数显示出来的那么难。。。)
注意到一个重要的事实:每条树边连接的两个村民之间至多进行一次操作,证明可以用归(xian)纳(ran)法。
这样的话就可以考虑树上DP,原问题要最小化操作次数,可能会想当然地列一个
f[x][i]
表示
x
为根的子树,进行一系列操作之后根节点可以给出/必须接受
只需要从上文的陷阱中跳出来,我们发现操作次数是
O(n)
的,那就可以考虑设
f[x][i]
表示
x
为根的子树,操作
来看转移,这个DP的决策为每条边是否进行操作,对每个点 x 依次考虑它的每个孩子,对于一条父子边 (x,y),有:
- 这条边加入图中
f[x][i]+f[y][j]→f′[x][i+j+1] - 如果 f[y][j]≥0 ,那么可以不要这条边,让树从这里断开: f[x][i]→f′[x][i+j]
而答案即为最小的 i 使得
f[root][i]≥0 。稍有常识的人就能看出,这是一个背包的模型,而且 x 为根的子树的背包大小恰为
size[x] ,根据树上背包按 size 合并的理论,不难得到复杂度 O(n2) 。Code
(感觉没有Code根本没人看。。。)
#include <bits/stdc++.h> #define rep(i, x, y) for (int i = (x), _ = (y); i <= _; ++i) #define down(i, x, y) for (int i = (x), _ = (y); i >= _; --i) #define x first #define y second #define mp make_pair #define pb push_back #define bin(x) (1<<(x)) using namespace std; typedef long long LL; typedef pair<int, int> pii; template<typename T> inline void upmax(T &x, T y) { x < y ? x = y : 0; } template<typename T> inline void upmin(T &x, T y) { x > y ? x = y : 0; } template<typename T> inline void read(T &x) { char c; while ((c = getchar()) < '0' || c > '9'); for (x = c - '0'; (c = getchar()) >= '0' && c <= '9'; x = x * 10 + c - '0'); } const int inf = 0x3f3f3f3f; const int N = 2005; vector<int> adj[N]; int n, X, val[N]; int size[N], f[N][N]; void treeDP(int x, int fa) { size[x] = 1; rep (i, 0, adj[x].size()-1) { if (adj[x][i] != fa) { treeDP(adj[x][i], x); size[x] += size[adj[x][i]]; } } static int g[2][N]; int t = 0, tot = 0; rep (i, 0, tot) g[t][i] = -inf; g[t][0] = val[x]; rep (i, 0, adj[x].size()-1) { int to = adj[x][i]; if (to != fa) { rep (i, 0, tot+size[to]) g[t ^ 1][i] = -inf; rep (j, 0, size[to]-1) { int v = f[to][j]; rep (i, 0, tot) { upmax(g[t ^ 1][i + j + 1], g[t][i] + v); } } int mi = 0; while (mi < size[to] && f[to][mi] < 0) ++mi; if (mi < size[to]) { rep (i, 0, tot) upmax(g[t ^ 1][i + mi], g[t][i]); } tot += size[to]; t ^= 1; } } rep (i, 0, tot) f[x][i] = g[t][i]; } int main() { #ifdef LX_JUDGE freopen("in.txt", "r", stdin); #endif read(n), read(X); int x, y; rep (i, 1, n) { read(val[i]); val[i] = X - val[i]; } rep (i, 2, n) { read(x), read(y); adj[x].pb(y); adj[y].pb(x); } treeDP(1, 0); int cur = 0; while (f[1][cur] < 0) ++cur; cout << cur << endl; return 0; }