1. 程式人生 > >洛谷 P2014 選課 樹形依賴揹包

洛谷 P2014 選課 樹形依賴揹包

題目描述

在大學裡每個學生,為了達到一定的學分,必須從很多課程裡選擇一些課程來學習,在課程裡有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有N門功課,每門課有個學分,每門課有一門或沒有直接先修課(若課程a是課程b的先修課即只有學完了課程a,才能學習課程b)。一個學生要從這些課程裡選擇M門課程學習,問他能獲得的最大學分是多少?

輸入輸出格式

輸入格式:  

第一行有兩個整數N,M用空格隔開。(1<=N<=300,1<=M<=300)

接下來的N,I+1行包含兩個整數kisi, ki表示第I門課的直接先修課,si表示第I門課的學分。若ki=0表示沒有直接先修課(

1<=ki<=N, 1<=si<=20)。

輸出格式:  

只有一行,選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

後還有-1,因為子節點和父節點之間也有一條邊)+edge[i].w(是存vu之間邊的權值(學分)),取個max就可以了,其實主要是第一層迴圈很難想到,就是你走的這個點,你要去搜索到它的所有子節點所有情況,你才能保證當前這點取得是最大學分)

程式碼實現:

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