洛谷 P2014 選課 樹形依賴揹包
阿新 • • 發佈:2018-12-13
題目描述
在大學裡每個學生,為了達到一定的學分,必須從很多課程裡選擇一些課程來學習,在課程裡有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有N門功課,每門課有個學分,每門課有一門或沒有直接先修課(若課程a是課程b的先修課即只有學完了課程a,才能學習課程b)。一個學生要從這些課程裡選擇M門課程學習,問他能獲得的最大學分是多少?
輸入輸出格式
輸入格式:
第一行有兩個整數N,M用空格隔開。(1<=N<=300,1<=M<=300)
接下來的N行,第I+1行包含兩個整數ki和si, ki表示第I門課的直接先修課,si表示第I門課的學分。若ki=0表示沒有直接先修課(
輸出格式:
只有一行,選M門課程的最大得分。
輸入輸出樣例
輸入樣例#1: 複製
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
輸出樣例#1: 複製
13
演算法分析:
書上思想講的完美,而且兩本書一模一樣的,誰抄誰的,反正我拍照
這裡是講解狀態轉移方程的版塊,f[u][j]表示此時走到樹的第u個點在儲存j門課下的最大學分,其一定等於f[v][k](它的子節點在k門課下的最大學分)+f[u][j-k-1](它本身除去這一個子節點外能夠上的課的門數,不要忘記在j-k
程式碼實現:
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <stack> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; const int N=310; struct node { int v;///終端點 int next;///下一條同樣起點的邊號 int w;///權值 }edge[N*2];///無向邊,2倍 int head[N];///head[u]=i表示以u為起點的所有邊中的第一條邊是 i號邊 int tot; ///總邊數 void add(int u,int v,int w) { edge[tot].v=v; edge[tot].w=w; edge[tot].next=head[u]; head[u]=tot++; } int n,m; int dp[N][N]; ///dp[i][j]表示節點i保留j個枝條的所剩蘋果最大值 int dfs(int u,int fa) ///求出每個節點子樹下的最大距離和次大距離 { int num=0; ///num表示u節點的子節點數目 for(int i=head[u];i!=-1;i=edge[i].next) { int v= edge[i].v; if(fa==v) continue; num+=dfs(v,u)+1; for(int j=min(m,num);j>=1;j--) for(int k=j-1;k>=0;k--) { dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[v][k]+edge[i].w); } } return num; } int main() { scanf("%d%d",&n,&m); memset(head,-1,sizeof(head)); memset(dp,0,sizeof(dp)); tot=0; for(int i=1;i<=n;i++) { int u,w; scanf("%d%d",&u,&w); add(u,i,w); } dfs(0,-1); printf("%d\n",dp[0][m]); return 0; }