1. 程式人生 > >CH5402 選課【樹形DP】【背包】

CH5402 選課【樹形DP】【背包】

tro 虛擬 直接 amp ios max bits div 總數

5402 選課 0x50「動態規劃」例題

描述

學校實行學分制。每門的必修課都有固定的學分,同時還必須獲得相應的選修課程學分。學校開設了 N(N≤300) 門的選修課程,每個學生可選課程的數量 M 是給定的。學生選修了這 M 門課並考核通過就能獲得相應的學分。
在選修課程中,有些課程可以直接選修,有些課程需要一定的基礎知識,必須在選了其他的一些課程的基礎上才能選修。例如《Windows程序設計》必須在選修了《Windows操作基礎》之後才能選修。我們稱《Windows操作基礎》是《Windows程序設計》的先修課。每門課的直接先修課最多只有一門。兩門課可能存在相同的先修課。
你的任務是為自己確定一個選課方案,使得你能得到的學分最多,並且必須滿足先修條件。假定課程之間不存在時間上的沖突。

輸入格式

輸入文件的第一行包括兩個整數N、M(中間用一個空格隔開)其中1≤N≤300,1≤M≤N。
以下N行每行代表一門課。課號依次為1,2,…,N。每行有兩個數(用一個空格隔開),第一個數為這門課先修課的課號(若不存在先修課則該項為0),第二個數為這門課的學分。學分是不超過10的正整數。

輸出格式

輸出文件只有一個數,實際所選課程的學分總數。

樣例輸入

7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2

樣例輸出

13

題意:

從n門課中選出m門課,使得他們的學分和最大。有的課程有先修課。

思路:

n門課構成了一個森林,給他們添加一個編號為0的虛擬節點,表示沒有先修課的課程的先修課。

dp[x][t]表示在以x為根的樹中選出t門能獲得的最高學分。他是由他的子樹的最大值加上自己的學分得來。

實際上是一個分組背包模型。有p|son(X)|組物品,每組物品有t-1個,其中第i組的第j個物品的體積為j,價值為dp[yi,j],背包的總容積為t-1。(yi是x的兒子)我們要從每組中選出不超過1個物品,使得物品體積不超過t-1的前提下,物品價值總和最大。x=0是一個特例。

背包類樹形DP,又稱有樹形依賴的背包問題。除了以“節點編號”作為樹形DP的幾階段,通常我們也像線性DP一樣,把當前背包的“體積”作為第二維狀態。

 1 //#include <bits/stdc++.h>
 2 #include<iostream>
 3 #include<cmath>
 4
#include<algorithm> 5 #include<stdio.h> 6 #include<cstring> 7 #include<vector> 8 #include<map> 9 10 #define inf 0x3f3f3f3f 11 using namespace std; 12 typedef long long LL; 13 14 int n, m; 15 const int maxn = 305; 16 vector<int>son[maxn]; 17 int sco[maxn]; 18 int dp[maxn][maxn]; 19 20 void dfs(int x) 21 { 22 dp[x][0] = 0; 23 for(int i = 0; i < son[x].size(); i++){ 24 int y = son[x][i]; 25 dfs(y); 26 for(int t = m; t >= 0; t--){//當前背包體積 27 for(int j = t; j >= 0; j--){//選課門數(組內物品) 28 if(t - j >= 0) 29 dp[x][t] = max(dp[x][t], dp[x][t - j] + dp[y][j]); 30 } 31 } 32 } 33 if(x != 0){ 34 for(int t = m; t > 0; t--){ 35 dp[x][t] = dp[x][t - 1] + sco[x]; 36 } 37 } 38 } 39 40 int main() 41 { 42 scanf("%d%d", &n, &m); 43 for(int i = 1; i <= n; i++){ 44 int f; 45 scanf("%d%d", &f, &sco[i]); 46 son[f].push_back(i); 47 } 48 49 dfs(0); 50 printf("%d\n", dp[0][m]); 51 return 0; 52 }

CH5402 選課【樹形DP】【背包】