P1273 有線電視網 - 樹上背包
阿新 • • 發佈:2018-10-14
inline clu += line 存在 sca cpp 臨時 數組下標
樹上背包看作分組背包就好了,收益臨時變成負數也是可以的,並且收益的數值也很大,所以不再讓收益當下標,放到數組裏保存,設f[x][t]表示以x為根的子樹中選擇t個人觀看節目,電視臺的最大收益(讓你求什麽反而不一定要存在數組裏面,可能是設為數組下標再判斷可行性)
這題比較特殊,一般分組背包是過不了這麽大數據的,但是因為實際有效的葉子節點十分少,所以可以優化(看註釋)
另外註意f數組初始值的設定
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> using namespace std; #define debug(x) cerr << #x << "=" << x << endl; const int MAXN = 3000 + 10; const int INF = (1<<30) / 3; // 這裏十分的坑。。。 一開始寫成1<<30/3 先運算30/3,結果都變成-8了。。。 int n,m,f[MAXN][MAXN],last[MAXN],ans,edge_tot,vis[MAXN],son[MAXN]; struct Edge{ int u,v,w,to; Edge(){} Edge(int u, int v, int w, int to) :u(u), v(v), w(w), to(to) {} }e[MAXN * 2]; inline void add(int u, int v, int w) { e[++edge_tot] = Edge(u, v, w, last[u]); last[u] = edge_tot; } void dfs(int x) { vis[x] = 1; if(n-m+1 <= x && x <= n) { son[x] = 1;//這裏。。。註意並不是son[x]傳統子樹大小了,而是包含多少用戶節點 } for(int i=last[x]; i; i=e[i].to) { int v = e[i].v, w = e[i].w; if(vis[v]) continue; dfs(v); son[x] += son[v]; for(int t=son[x]; t>=0; t--) {//因為son[x]比較小,所以這裏優化確實是很大的 for(int j=0; j<=son[v]; j++) { f[x][t] = max(f[x][t], f[x][t-j] + f[v][j] - w);//若有選0個的子樹那麽根本不用花費這w,所以選0個的不會有影響 } } } } int main() { scanf("%d%d", &n, &m); for(int i=1; i<=n-m; i++) { int k,a,c; scanf("%d", &k); for(int j=1; j<=k; j++) { scanf("%d%d", &a, &c); add(i, a, c); add(a, i, c); } } for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) { f[i][j] = -INF; } // f[i][0] = 0; 這裏不設為0是可以的,在上面的轉移中若有選0個的子樹出現一定不會影響已有的最優收益 } for(int i=n-m+1; i<=n; i++) { scanf("%d", &f[i][1]); } dfs(1); for(int i=0; i<=m; i++) { if(f[1][i] >= 0) { ans = max(ans, i); } } printf("%d\n", ans); return 0; }
P1273 有線電視網 - 樹上背包