題解/算法 {2920. 收集所有金币可获得的最大积分}

題解/算法 {2920. 收集所有金币可获得的最大积分}

@LINK: https://leetcode.cn/problems/maximum-points-after-collecting-coins-from-all-nodes/;

首先讀題要仔細, 首先把所有的測試樣例 按照你的思路 看能不能對應上;
把操作2看做是(把以c為樹根的子樹的所有節點 都執行A[c]/=2, 即該子樹的價值為 所有節點的和除以2, 即一旦c執行操作2 那麼他的所有兒子節點都必須執行操作2), 這你就錯誤了;
仔細讀題 最起碼把樣例通過了, 按你這種思路 樣例都通過不了, 他說的是 當前點的價值是A[c]/=2 同時把他的所有兒子都變成A[i]/=2, 但他兒子的價值 不一定就是A[i]/=2 而是要單獨考慮的, 即他也要選擇進行操作1/2;
因此 如果寫成DP(i,j)表示以i為根的子樹 且i點進行操作j的 最大價值, 這就錯誤了, 因為i點 他會受到祖先節點的影響 即如果他的祖先節點執行了操作2 那麼此時A[i]的值 會是A[i]/{2/4/8/...} 而不是A[i];

定義DP(i,j)表示以i為根的子樹 且他的祖先節點裡已經執行了j次操作2, 即此時i點的節點值為A[i]/ (1<<j), 然後再考慮該點 執行操作1/操作2;

如果這樣定義, 這個j會很大 最大是N1e5, 而所有節點都是<=1e4的, 即當j=15時 其實此時以i為根的子樹 所有節點都是0了已經(當然不意味著 他們就一定是選擇操作1/2, 不考慮這個問題) 我們這裡關注的重點是: [此時j=15的這個數 他和任意的j>15的樹 是完全一樣的], 因為完全一樣 自然答案也一樣, 即DP(i,j>15) = DP(i,15);

#錯誤#: DP(i,j)(1<<j) > A[i]時, 此時A[i]/ (1<<j) == 0, 所以此時所有的DP(i,>j) == DP(i,j);
這是錯誤的;
因為我們判斷的是 以i為根的這個子樹 是否相同, 而不是i這個節點是否相同, 比如i這個節點 A[i]=1, 那麼當j>=1時 此時A[i]就等於0了, 但是 他的兒子 比如是A[j]=1e4, 那麼顯然1e4/2 != 1e4/4;
因此 我們判斷是 所有點的最大值的最大的j, 而不是當前點的;

static int DP[ 100005][21];
memset( DP, -1, sizeof( DP));
function<int(int,int,int)> Dfs = [&]( int _cur, int _fa, int _div){
    if( _div >= 15){
        _div = 15;
    }
    if( DP[_cur][ _div] != -1){ return DP[_cur][ _div];}
    
    auto & curDP = DP[_cur][ _div];
    
    int a0 = (A[_cur] / (1 << _div)) - K; // 操作1
    int a1 = (A[_cur] / (1 << _div) / 2); // 操作2
    for( int nex, edge = G.Head[_cur]; ~edge; edge = G.Next[edge]){
        nex = G.Vertex[edge];
        if( nex == _fa){ continue;}
        a0 += Dfs( nex, _cur, _div);
        a1 += Dfs( nex, _cur, _div + 1);
    }
    curDP = max( a0, a1);
    return curDP;
};
return Dfs( 0, -1, 0);

以上是正確的代碼;
看一個錯誤的代碼; 如果你把上面的記憶化DP, 寫成是: 把DFS的返回值去掉 然後直接去使用DP[nex][_cur], 即把a0 += Dfs( nex, _cur, _div); 改成是Dfs( nex, _cur, _div); a0 += DP[nex][_div];
這就錯誤的;
當然 對於一般的記憶化DP, 這是等價的 沒問題的, 但這裡比較特殊, 因為你DFS(i,j) 並不一定對應著就是DP[i][j] 這是這個題獨特之處, 比如當j==15時, 此時你會調用DFS(nex, 16) 上面正確的代碼 他返回的答案 其實是DP[nex][15] 而不是DP[nex][16], 但是你這裡錯誤的代碼 他是a1 += DP[nex][ _div + 1], 即此時越界了 因為DP(nex, 16) 這個值 並在我們的DP裡面!
. 那麼你可能認為 應該是WA, 而事實是 他會是TLE… 這是因為 一個極端的例子 1-2-3-4-5-...-N, 此時你調用DP[N][16] 他等於初始值 即memset的值 為-1, 因此對於DP(N-1,15) 假如他的a0值為負數 然後此時a1 = DP[N][16] = -1, 因此 導致DP(N-1,15) = max(-1, <0) = -1, 因此 並沒有做到記憶化 以後還會重複訪問這些點 所以是TLE;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值