P2014 选课 (树上背包)

树上背包:

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

它基本上是这个样子的:

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

void dfs(int fa){
    for(int i=0;i<son[fa].size();i++){
        int ny=son[fa][i];
        dfs(ny);
        for(int j=m+1;j>=1;j--){
            for(int k=j-1;k>=0;k--){
                f[fa][j]=max(f[fa][j],f[fa][j-k]+f[ny][k]);
            }
        } 
    }
}

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

#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;
}

猜你喜欢

转载自www.cnblogs.com/-Ackerman/p/12337944.html