P2014 选课 (树型背包模版)

树上背包:

树形背包就是原始的树上动归+背包,一般用来处理以一棵树中选多少点为扩展的一类题,基本做法与树上dp无异,不过在状态转移方程中会用到背包的思想。

它基本上是这个样子的:

存图),然后dfs进去补全子节点的信息,f数组的意思是以fa为中转点,找出fa往下的可取1~j个点时各自的最大收益。

for(int i=1;i<=son;i++){//枚举所有儿子
  dfs(son[i]);//先处理儿子
  for(int j1=m;j1>=0;j1--){//枚举当前点用掉了多少容量(正着枚举会变成完全背包)
    for(int j2=0;j2<=j1;j2++){//枚举这个儿子分配多少
      dp[i][j1]=max(dp[i][j1],dp[i][j1-j2]+dp[son[i]][j2]);//更新状态
    }
  }
}

选课这题基本上就是树上背包的板子题了:

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>
#include <bitset>
#include <cmath>

#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1

const double eps = 1e-10;
const int maxn = 310;
const LL mod = 1e9 + 7;

int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;}
using namespace std;

struct edge {
    int v,nxt;
}e[maxn];

int head[maxn],w[maxn],f[maxn][maxn];
int fa[maxn];
int cnt;
int n,m;


void add_edge(int u,int v) {
    e[++cnt].v = v;
    e[cnt].nxt = head[u];
    head[u] = cnt;
}

void dfs(int x) {
    for (int i = head[x];~i;i = e[i].nxt) {
        int v = e[i].v;
        dfs(v);
        for (int j = m+1;j >= 1;j--) {
            for (int k = j-1;k >= 0;k--)
                f[x][j] = max(f[x][j],f[v][k]+f[x][j-k]);
        }
    }
}


int main() {
    cnt = 0;
    memset(head,-1, sizeof(head));
    cin >> n >> m;
    for (int i = 1;i <= n;i++) {
        int u,v;
        cin >> u >> v;
        fa[i] = u;
        add_edge(u,i);
        f[i][1] = v;
    }
    dfs(0);
    cout << f[0][m+1] << endl;
    return 0;
}

还有一种多叉树转二叉树的方法:

#include <iostream>
#include <algorithm>
#include <string>
#include <string.h>
#include <vector>
#include <map>
#include <stack>
#include <set>
#include <queue>
#include <math.h>
#include <cstdio>
#include <iomanip>
#include <time.h>
#include <bitset>
#include <cmath>

#define LL long long
#define INF 0x3f3f3f3f
#define ls nod<<1
#define rs (nod<<1)+1

const double eps = 1e-10;
const int maxn = 310;
const LL mod = 1e9 + 7;

int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;}
using namespace std;

int f[maxn][maxn],mark[maxn];
int l[maxn],r[maxn];
bool s[maxn][maxn];
int w[maxn];
int n,m;

inline void pre(int x) {    // 多叉树转二叉树
    mark[x] = 1;
    for (int i = 1;i <= n+1;i++) {
        if (!mark[i] && s[x][i]) {
            r[i] = l[x];
            l[x] = i;
            pre(i);
        }
    }
}

inline void dfs(int x) {
    if (l[x])
        dfs(l[x]);
    if (r[x])
        dfs(r[x]);
    for (int y = 0;y <= m;y++) {
        f[x][y] = f[r[x]][y];
        for (int k = 0;k <= y-1;k++)
            f[x][y] = max(f[x][y],f[l[x]][k]+f[r[x]][y-k-1]+w[x]);
    }

}

int main() {
    ios::sync_with_stdio(false);
    cin >> n >> m;
    for (int i = 1;i <= n;i++) {
        int a,b;
        cin >> a >> b;
        w[i] = b;
        if (a == 0)
            a = n+1;
       s[a][i] = true;
    }
    pre(n+1);
    dfs(n+1);
    cout << max(f[l[n+1]][m],f[r[n+1]][m]) << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/-Ackerman/p/12337944.html