vijos P1180 選課 樹形dp

題目地址:p1180選課
多叉的情況不好做,所以可以轉換成二叉樹.
那麼設dp[i][j]爲以i爲根節點的子樹上選擇j門課.
dp[i][j] = max(dp[right][j], dp[left][k - 1] +dp[right][j - k] + credit[i] | 1<= k <= j)

轉移方程時一定要注意右兒子都是兄弟不是兒子

因爲轉換成了二叉樹,所以任何節點i的右節點是其多叉樹上的兄弟節點,左節點纔是多叉樹的子樹.
所以dp[right][j]爲把j課全部安排在兄弟節點子樹上,dp[left][k - 1]爲安排一門課在i上,安排k - 1門在子數上,安排j - k門在兄弟節點子樹上.
總的複雜度爲O(n^3)
轉自 螞蟻大戰大象

#include <cstdio>  
#include <vector>  
#include <memory.h>  
using namespace std;  
const int MAX =305;  

vector<int> t[MAX];  
int credit[MAX], L[MAX], R[MAX];  
int dp[MAX][MAX];  

int dfs(int i, int j){  
    if(i == -1)return 0;  
    else if(j <= 0)return 0;  
    else if(dp[i][j] != -1)return dp[i][j];  

    dp[i][j] = dfs(R[i], j);  
    for(int m = 1; m <= j; ++m){  
        dp[i][j] = max(dp[i][j], dfs(L[i], m - 1) + dfs(R[i], j - m) + credit[i]);  
    }  

    return dp[i][j];  
}  
int main(int argc, char const *argv[]){  
    int N, M;  
    memset(L, -1, sizeof(L));  
    memset(R, -1, sizeof(R));  
    memset(dp, -1, sizeof(dp));  
    scanf("%d%d", &N,  &M);  
    for(int i = 1; i <= N; ++i){  
        int pre;  
        scanf("%d %d", &pre, &credit[i]);  
        t[pre].push_back(i);  
    }  

    for(int i = 0 ; i <= N; ++i){  
        if(t[i].size() > 0){  
            L[i] = t[i][0];  
            int rc = t[i][0];  
            for(int j = 1; j < t[i].size();++j){  
                R[rc] = t[i][j];  
                rc = t[i][j];  
            }  
        }  
    }  

    printf("%d\n", dfs(0, M + 1));  
    return 0;  
} 

注意:輸出要用dp[0][M+1],因爲多了一個根節點0

發佈了35 篇原創文章 · 獲贊 5 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章