树形背包小结

已知(2+1)?种做法

分别是:

  • 按dfs序倒推

Link

BZOJ2427代码

int sta[N], dfn[N], low[N], w[N], val[N], dp[N][505], n, m, top, W[N], V[N], fa[N], l[N], cnt[N], siz[N], bel[N], idx;
vint G[N];
 
bitset<N>ins, vis;
void tarjan(int u) {
  static int tar = 0, idx = 0;
  ins[sta[++top] = u] = 1, dfn[u] = low[u] = ++idx;
  for (vint::iterator v = G[u].begin(); v != G[u].end(); ++v) {
    if (!dfn[*v]) {
      tarjan(*v);
      low[u] = min(low[u], low[*v]);
    }
    else if (ins[*v]) low[u] = min(low[u], dfn[*v]);
  }
  if (dfn[u] == low[u]) {
    int v; ++tar;
    do {
      ++cnt[tar];
      ins[v = sta[top--]] = 0, bel[v] = tar;
      w[tar] += W[v], val[tar] += V[v];
    }
    while (u != v);
  }
}
void dfs(int u) {
  l[++idx] = u, siz[u] = 1;
  for (vint::iterator v = G[u].begin(); v != G[u].end(); ++v) dfs(*v), siz[u] += siz[*v];
}
 
void init() {
 
}
 
void solve() {
  in, n, m;
  lo1(i, n) in, W[i];
  lo1(i, n) in, V[i];
  lo1(i, n) G[fa[i] = in].pb(i);
  lo1(i, n) if (!dfn[i]) tarjan(i);    
  lo0(i, n + 1) G[i].clear();
  lo1(i, n)
  if (cnt[bel[i]] > 1 && !vis[bel[i]]) G[0].pb(bel[i]), vis[bel[i]] = 1; else if (cnt[bel[i]] == 1) G[bel[fa[i]]].pb(bel[i]);
  dfs(0);
  memset(dp, 0xbf, sizeof dp);
  dp[idx + 1][0] = 0;
  dl1(i, idx) {
    int x = l[i];
    lo0(j, m + 1) {
      chmax(dp[i][j], max(dp[i + siz[x]][j], 0));
      if (w[x] <= j) chmax(dp[i][j], dp[i + 1][j - w[x]] + val[x]);
    }
  }
  int ans = 0;
  lo1(i, m) chmax(ans, dp[1][i]);
  out, ans;
}
 
int main() {
#ifdef QvvQ
  // freopen("data.in", "r", stdin);
  // freopen("data.out", "w", stdout);
  Dbg = 1;
#endif
  int T = 1;
  while (T--) init(), solve();
#ifdef QvvQ
  fprintf(stderr, "\ntime:%.5fms", clock() * 1000.0 / CLOCKS_PER_SEC);
#endif
  return 0;
}
  • 2009集训队论文中的写法

dp(u res)表示选择了根到u的路径上的所有点 再在u左侧(已经遍历过的部分)和u的子树内选择重量最大为res的物品的最大价值

BZOJ2427代码

 w[N], val[N], dp[N][505], n, m, top, W[N], V[N], fa[N], cnt[N], bel[N];
vint G[N];
 
bitset<N>ins, vis;
void tarjan(int u) {
  static int tar = 0, idx = 0;
  ins[sta[++top] = u] = 1, dfn[u] = low[u] = ++idx;
  for (vint::iterator v = G[u].begin(); v != G[u].end(); ++v) {
    if (!dfn[*v]) {
      tarjan(*v);
      low[u] = min(low[u], low[*v]);
    }
    else if (ins[*v]) low[u] = min(low[u], dfn[*v]);
  }
  if (dfn[u] == low[u]) {
    int v; ++tar;
    do {
      ++cnt[tar];
      ins[v = sta[top--]] = 0, bel[v] = tar;
      w[tar] += W[v], val[tar] += V[v];
    }
    while (u != v);
  }
}
void DP(int u, int res) {
  if (!res) return ;
  for (vint::iterator it = G[u].begin(); it != G[u].end(); ++it) {
    copy(dp[u] + 1, dp[u] + 1 + res, dp[*it] + 1);
    DP(*it, res >= w[*it] ? res - w[*it] : res);
    dl(j, res, w[*it]) chmax(dp[u][j], dp[*it][j - w[*it]] + val[*it]);
  }
}
 
void init() {
 
}
 
void solve() {
  in, n, m;
  lo1(i, n) in, W[i];
  lo1(i, n) in, V[i];
  lo1(i, n) G[fa[i] = in].pb(i);
  lo1(i, n) if (!dfn[i]) tarjan(i); 
  lo0(i, n + 1) G[i].clear();
  lo1(i, n)
  if (cnt[bel[i]] > 1 && !vis[bel[i]]) G[0].pb(bel[i]), vis[bel[i]] = 1; else if (cnt[bel[i]] == 1) G[bel[fa[i]]].pb(bel[i]);
  DP(0, m - w[0]);
  out, dp[0][m] + val[0];
}
 
int main() {
#ifdef QvvQ
  // freopen("data.in", "r", stdin);
  // freopen("data.out", "w", stdout);
  Dbg = 1;
#endif
  int T = 1;
  while (T--) init(), solve();
#ifdef QvvQ
  fprintf(stderr, "\ntime:%.5fms", clock() * 1000.0 / CLOCKS_PER_SEC);
#endif
  return 0;
}

另一种在dfs过程中枚举 i <- siz[u] to 0j <- 0 to siz[v]的做法 (好像跟上面两种不通用,如果通用求指正)

一个这个写法的题目

posted @ 2019-02-27 21:01  QvvQ  阅读(177)  评论(0编辑  收藏  举报