【基礎算法】選課

蒟蒻的世界

目錄

題目

描述

輸入

輸出

樣例輸入

樣例輸出

分析

狀態定義

代碼


題目

描述

學校實行學分制。每門的必修課都有固定的學分,同時還必須獲得相應的選修課程學分。 學校開設了 N 門的選修課程,每個學生可選課程的數量 M 是給定的。學生選修了這M門課並考覈通過就能獲得相應的學分。 在選修課程中,有些課程可以直接選修,有些課程需要一定的基礎知識,必須在選了其它的一些課程的基礎上才能選修。例如《Frontpage》必須在選修了《Windows 操作基礎》之後才能選修。我們稱《Windows 操作基礎》是《Frontpage》的先修課。每門課的直接先修課最多隻有一門。兩門課也可能存在相同的先修課。每門課都有一個課號,依次爲1,2,3,…。例如:

表中 1 是 2 的先修課,2 是 3、4 的先修課。如果要選 3,那麼 1 和 2 都一定已被選修過。 你的任務是爲自己確定一個選課方案,使得你能得到的學分最多,並且必須滿足先修課優先的原則。假定課程之間不存在時間上的衝突。

輸入

第1行:2個空格分開的整數 N、M,其中 1≤N≤300,1≤M≤N。

接下來N 行每行代表一門課。課號依次爲1,2,…,N。每行有2個空格分開的整數,第一個數爲這門課先修課的課號(若不存在先修課則該項爲 0),第二個數爲這門課的學分。 學分是不超過10 的正整數。

輸出

第1行:只有一個數,即實際所選課程的學分總數。

樣例輸入

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

樣例輸出

13

分析

顯然,這道題非常的魔性,個人覺得,它與沒有上司的舞會最大的不同在於只有他的父親來了,他的兒子纔可以來。最爲致命的是:在兒子可以多選的情況下,選的課程的數量還是有限的……

狀態定義

dp[i][j]:在前 i 門課程中選 j 門課程的最大得分

顯然這道題兩個兒子的值是可以算到一個父親裏的,所以這道題頗有 dp 的味道。

由於選擇了這門課程,所以他的兒子也需要加上這一部分的值,否則他的兒子就僅僅等於它的初值(恆定不變)

所以我們需要在進行 dp 之前將一些本就屬於兒子的東西傳給他(頗有繼承遺產的意味):

dp[son][i] = dp[father][i] + val[son];

其次在回溯過程中我們仍需要進行一次更新操作,也許在這次遞歸當中,某個兒子的值變大了,那麼我們離所需要的最大值又近了一步。

(也可說這是一次統計操作,將遞歸時傳遞出去時所獲得的最大值紛紛傳遞回來)

但值得注意的是:判斷本身與兒子最大值的時候,兒子的 j 需要減一( dp[x][j] = max(dp[x][j],dp[s][j - 1]);

爲什麼呢?因爲第 x 門課程已經被選中了,則作爲兒子,他可選擇的課程數就會比父親的少一個。

少選一個,很顯然吃虧呀,很多時候,他的值不會小一些嗎?其實在冥冥之中,異常 weisuo  的父親已將將他自己的值(獨門絕技)傳給兒子了

當你看了代碼後,發現:兒子的 0 號元素表示他自己的值,1號元素表示往前選取一個數的值。

for (reg int j = 0;j < tot; ++ j)
    dp[s][j] = dp[x][j] + val[s];

雖然同是將 tot - 1 個元素傳遞,但 + val[s] 偷偷摸摸地就把 son 的值加上去了

代碼

#include<cstdio>
#include<vector>
#define M 300
#define max(x,y) (x > y ? x : y)
#define reg register
using namespace std;
 
int n,m;
int dp[M + 5][M + 5],val[M + 5];
vector < int > G[M + 5];
 
void dfs(int x,int tot){
    if (tot <= 0)
        return ;
    for (reg int i = 0;i < G[x].size(); ++ i){
        int s = G[x][i];
        for (reg int j = 0;j < tot; ++ j)
            dp[s][j] = dp[x][j] + val[s];
        dfs(s,tot - 1);
        for (reg int j = 1;j <= tot; ++ j)
            dp[x][j] = max(dp[x][j],dp[s][j - 1]);
    }
}
 
int main(){
    scanf("%d%d",&n,&m);
    for (reg int i = 1;i <= n; ++ i){
        int x,y;
        scanf("%d%d",&x,&y);
        G[x].push_back(i);
        val[i] = y;
    }
    dfs(0,m);
    printf("%d\n",dp[0][m]);
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章