#動態規劃# hdu-4616-Game(樹形dp)

這個題,大佬說是水題,對我這種菜雞來說,看了好久才懂。。。。

 

#include <iostream>
#include <cstring>
#include <stdio.h>
#include <vector>

#define inf 0x3f3f3f3f

using namespace std;

/*
①如果k==c,那麼起點和終點至少有一個是陷阱(可能有些人會認爲終點一定會是陷阱,這樣是沒錯的,因爲起點和終點時相對的,你也可以把起點看做終點)。
②如果k<c,那麼起點和終點是否是陷阱是任意的,可以有也可以沒有。
*/

//這個題和CCF上做的第四題差不多,dp[i][j]做樹形dp是模板,這裏加了陷阱的條件,自然要多一維。
const int N = 50010;

vector<int> g[N];
int val[N],trap[N];
long long dp[N][4][2];

int T,n,c;
long long ans;

void dfs(int parent,int child){

    dp[child][trap[child]][trap[child]] = val[child];
    ans = max(ans,dp[child][trap[child]][trap[child]]);//是原來的路好,還是以child做起點更好???????????

    for(int i=0;i<g[child].size();i++){
        int v = g[child][i];

        if(v==parent) continue;

        dfs(child,v);

 /*=================================================================================================*/
        //有點像區間dp了,是把兩個鏈拼起來的
        //這裏應該和下面分開來看,這裏是單獨更新ans的過程,這裏並不需要考慮v是child子節點這個特殊關係
        //只有子節點纔可以拼接啊。

        //在上面的dfs中,已經更新了v及其子節點的dp值。
        for(int j=0;j<=c;j++){
            for(int k=0;j+k<=c;k++){
                //child和v起點都是陷阱
                //
                if(j+k<=c) ans = max(ans,dp[child][j][1]+dp[v][k][1]);
                //j+k==c時,不能執行這一句,因爲child和v起點都不是陷阱,不可能。
                if(j+k<c) ans = max(ans,dp[child][j][0]+dp[v][k][0]);

                //j==c時不執行這一句,因爲若執行,j==c,起點又不是陷阱,不可能,child還要往下拼接,不能是陷阱。
                if(j!=c) ans = max(ans,dp[child][j][0]+dp[v][k][1]);
                if(k!=c) ans = max(ans,dp[child][j][1]+dp[v][k][0]);
            }
        }
/*==============================================================================================*/

        //j+trap[child]  很可能就==c了!!!所以沒得打問題,下面的if只是不能用這裏更新罷遼,
        //把這裏更新的覆蓋掉。
        for(int j=0;j<c;j++){
            dp[child][j+trap[child]][0] = max(dp[child][j+trap[child]][0],dp[v][j][0]+val[child]);
            dp[child][j+trap[child]][1] = max(dp[child][j+trap[child]][1],dp[v][j][1]+val[child]);
        }

        //如果child不是陷阱,那麼起點肯定就是陷阱了。
        //那此時我們走線就是相反的,應該是從child走到起點。

        //那如果child是陷阱,那麼起點可以是也可以不是,這裏爲何不更新呢??
        //在上面for那裏更新了。
        if(!trap[child]) dp[child][c][1] = max(dp[child][c][1],dp[v][c][1]+val[child]);
    }
}
int main()
{
    scanf("%d",&T);
    while(T--){
        scanf("%d  %d",&n,&c);

        /*===========================新一輪,全清零=========================*/
        ans=0;
        for(int i=0;i<n;i++){
            g[i].clear();
        }
        memset(dp,-inf,sizeof(dp));//不能是0,得是-inf,爲啥?
        //詳情可見:https://blog.csdn.net/tri_integral/article/details/9499037   下方評論
        //因爲如果是0,這一句“dp[child][j+trap[child]][1] = max(dp[child][j+trap[child]][1],dp[v][j][1]+val[child]);”
        //0++val[child] 很可能會超過前面,但其實這個0不對。
        //仔細想想,此時正確的值不是0,而是這個數據無意義。(從理解的角度來說)
/*========================================================================================================*/
        for(int i=0;i<n;i++){
            scanf("%d %d",&val[i],&trap[i]);
        }

        int a=0,b=0;
        for(int i=0;i<n-1;i++){
            scanf("%d %d",&a,&b);
            g[a].push_back(b);
            g[b].push_back(a);
        }

        dfs(-1,0);

        printf("%lld\n",ans);
    }

    return 0;
}

 

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