洛谷P2014—选课(树形DP)

题目

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 a 是课程 b 的先修课即只有学完了课程 a,才能学习课程 b)。一个学生要从这些课程里选择 M 门课程学习,问他能获得的最大学分是多少?

输入输出

输入:第一行输入N,M。下面N行输入两个整数ki和si,分别为第i门课的先修课序号,第i门课的学分。
输出:选M门课的最大学分

思路

这道题输入的是森林,并不是一棵树,需要加一个结点,把每个子树连到根节点,构成一棵树,即加入0结点作为虚拟课程,学分为0。
状态转移数组
dp[i][j]为以i为根的子树选j门课程获得的最大学分。
0结点是必选的,所以需要m+1。
状态转移方程
dp[u][i](i>=1)初始化为s[u]为u的学分。
dp[u][j] = max(dp[u][j],dp[v][k] + dp[u][j-k]);

#include <iostream>
#include<algorithm>
#include<cmath>
using namespace std;

const int MAX_N = 310; //最多结点数
int tot;    //标记边的序号
int head[MAX_N],nxt[MAX_N],ver[MAX_N];  //建树要用到的数组
int dp[MAX_N][MAX_N];
int n,m,s[MAX_N];

void addedge(int u,int v){  //根据邻接表建树的过程
    ver[++tot] = v;
    nxt[tot] = head[u];
    head[u] = tot;
}

void dfs(int u){
    dp[u][0] = 0;
    for(int i = 1;i < m;i++)  dp[u][i] = s[u];  //初始化都加上本身的学分
    for(int i = head[u];i;i = nxt[i]){
        int v = ver[i];
        dfs(v);
        for(int j = m;j >= 1;j--){  //逆序枚举背包容量
            for(int k = j - 1;k >= 0;k--){  //枚举给子树分配多少容量
                dp[u][j] = max(dp[u][j],dp[v][k] + dp[u][j-k]);     //子树选k门课获得的学分加上本身选j-k门课获得的学分
            }
        }
    }
}

int main(){
	cin >> n >> m;
	int u;
	for(int i=1;i <= n;i++){
        cin >> u >> s[i];
        addedge(u,i);
	}
	m++;   //将0这门虚拟课程也算进去了
	dfs(0);
	cout << dp[0][m] ;
	return 0;
}

上一篇:AcWing 98. 分形之城


下一篇:PHP代码片段