有線電視網
阿新 • • 發佈:2018-08-24
方程 ring ++ edge mon 註意 pri ret math
有線電視網
題目大意:給出一棵樹,葉子結點增加一定的權值,經過減少一定的權值,求在總權值\(\geq0\)的情況下可以到達的葉子結點最多的數量.
樹上的背包問題
這樣來DP
- 狀態:\(f[i][j]\)為以\(i\)為根的子樹中,滿足\(j\)個客戶的需求所能獲得的最大收益
- 轉移方程:\(f[u][j] = max(f[u][j], f[u][j - k] + f[v][k] - e[i].val)\)
在樹上做背包,每搜到一個點就更新一遍,註意滾動數組,倒序
代碼
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> const int N = 3005; const int INF = 1147483640; struct Edge{ int to, next, val; }e[N]; int n, m, cnt; int son[N], head[N], money[N], f[N][N], sz[N]; inline void addedge(int x, int y, int z){ e[++cnt].to = y; e[cnt].val = z; e[cnt].next = head[x]; head[x] = cnt; return; } int dfs(int x){ if(x > n - m){ f[x][1] = money[x];//如果是葉子結點,改變初始值 return sz[x] = 1; } for(int i = head[x], v, l_sum = 0; i; i = e[i].next){ v = e[i].to; l_sum = dfs(v); sz[x] += l_sum; for(int j = sz[x]; j; --j)//這個點包含的客戶,每次都更新下 for(int k = 1; k <= l_sum; ++k){//f[v][k]中的k限制於sz[v] if(j - k < 0 ) break; f[x][j] = std::max(f[x][j], f[x][j - k] + f[v][k] - e[i].val); } } return sz[x]; } int main(){ scanf("%d %d", &n, &m); for(int i = 1; i <= n - m; ++i){ scanf("%d", &son[i]); for(int j = 1, x, y; j <= son[i]; ++j){ scanf("%d %d", &x, &y); addedge(i, x, y); } } for(int i = n - m + 1; i <= n; ++i) scanf("%d", &money[i]); for(int i = 1; i <= n; ++i){ for(int j = 1; j <= m; ++j){ f[i][j] = -INF; } } dfs(1); for(int i = m; i; --i){ if(f[1][i] >= 0){ printf("%d", i); break; } } return 0; }
錯誤qwq
- 如果在有返回值的函數後面不加\(return\),而寫其他東西,會很奇怪
- 把\(f[i][j]\)初始化為極小值後,轉移的時候再減去邊權會炸掉,變為正的,註意不要初始化太小了
有線電視網