Riv--ツリーDP

タイトル

トピックリンク

考え

全体の問題は考慮すべきより多くのポイントを持っているように見えます

DP意図最初の方程式

dp[i][j]表示第i个结点所在子树建j个伐木场的最小花费
dp[i][j] = min(dp[v][k] + cost)

それは非常によさそうだ、と時間の複雑さは高くありませんが、質問です

費用はどのようにああカウント

残りの木材は距離を行くことを知っていないかわかりません

そう教師は前記の$ \色{赤} \テキスト{D} $に

定义dp[i][j][k]表示第i个结点所在子树建j个伐木场且最近的一个伐木场在k

私はその後、$ \ {色青} \ {テキストアウト} $は望んでいません。

だから、問題へのバレーロサンゼルスのソリューションを参照してください、それは非常に明確ではないと言う、特にこれは、別の書き込み中にします

4次元の定義

dp[i][j][k]表示第i个结点最近且建了伐木场的为j 共建k个的最小花费 1 表示在i点建了

上記は、祖先ノード上で、最近の製材所を指していることに注意してください

レコード祖先は考えます

ancestor[++tot] = x;放在dfs里的首行
tot--;放在末行

非常によく理解

距離及び木材の数を記録します

ただ、土地が今に行く必要があることを知っていて、それがこのように表現することができる場所

u:现在所在之地
v:应前往之地
dep[i]记录i到根1的距离
即dis(u,v) = |dep[v] - dep[u]|

製材所へここから出荷された使い捨て木材の数は、それが中間の状態を記録しません。

wood[i]表示i点的木材

そして、それは、状態遷移方程式の中で最も重要です

dp[x][fa][k][0] = Min(dp[x][fa][k][0],dp[v][fa][l][0] + dp[x][fa][k - l][0]);
dp[x][fa][k][1] = Min(dp[x][fa][k][1],dp[v][x][l][0] + dp[x][fa][k - l][1]);    

そして、非常に特別なものがあります

for(j = 1;j <= tot;j++)
{
//j的顺序无所谓
    int fa = ancestor[j];
    for(k = maxk;k >= 0;k--)
    {
    //k的顺序不能倒,因为不能用更新过的状态来更新(会让dp[v]多次被计算)
        dp[x][fa][k][0] += dp[v][fa][0][0];
        dp[x][fa][k][1] += dp[v][x][0][0];
        /*因为没有初始化dp,最先它自己一定为0
        如果不单独提出来处理,会保持0的状态 不被更新
        */
        for(l = 0;l <= k;l++)
        {
        //这个l顺序也无所谓
            dp[x][fa][k][0] = Min(dp[x][fa][k][0],dp[v][fa][l][0] + dp[x][fa][k - l][0]);
            dp[x][fa][k][1] = Min(dp[x][fa][k][1],dp[v][x][l][0] + dp[x][fa][k - l][1]);    
        }
    }
}

もう一つのポイントは、知っておく必要があります

第四维 表示的是当前结点是否有伐木场
在回溯后就没用了,因此需要进行合并,且还得将当前结点的木材运至方程中的fa
for(i = 1;i <= tot;i++)
{
//i的顺序无所谓
    int fa = ancestor[i];
    for(j = 0;j <= maxk;j++)
    {
    //j的顺序也无所谓
        if(j >= 1)
            dp[x][fa][j][0] = Min(dp[x][fa][j][0] + wood[x] * (dep[x] - dep[fa]),dp[x][fa][j - 1][1]);
        //单独处理是因为0 - 1 = -1 造成数组越界
        /*
        那为什么会有-1的操作了
        原题解说的挺清楚的
        先前算的dp[x][fa][k][1]是用dp[v][x][l][0] + dp[x][fa][k - l][1]来更新的
        而l + k - l = k < k + 1,但我们此时表示的是k + 1 的状态,故需-1
        也可以在上诉方程中加个-1
        改成dp[v][x][l][0] + dp[x][fa][k - l + 1][1]
        */
        else dp[x][fa][j][0] += wood[x] * (dep[x] - dep[fa]);
        //先前的记录
    }
}

コード

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
  
inline void Read(int &x)
{
    x = 0;
    char a = getchar();
    bool f = 0;
    while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
    while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
    if(f) x *= -1;
}
const int MAXN = 202;
vector<int> G[MAXN],Road[MAXN];
int maxk,wood[MAXN],ancestor[MAXN],tot,dep[MAXN],dp[MAXN][MAXN][52][2];
bool vis[MAXN];
//dp[i][j][k]表示第i个结点最近且建了伐木场的为j 共建k个的最小花费 1 表示在i点建了 

template<typename T>
inline T Min(T a,T b) {if(a < b) return a;return b;}
inline void dfs(int x)
{
    int i,j,k,l;;
    ancestor[++tot] = x;
    for(i = 0;i < G[x].size();i++)
    {
        int v = G[x][i],w = Road[x][i];
        if(!vis[v])
        {
            vis[v] = 1;
            dep[v] = dep[x] + w; 
            dfs(v);
            for(j = tot;j >= 1;j--)
            {
                int fa = ancestor[j];
                for(k = maxk;k >= 0;k--)
                {
                    dp[x][fa][k][0] += dp[v][fa][0][0];
                    dp[x][fa][k][1] += dp[v][x][0][0];
                    for(l = k;l >= 0;l--)
                    {
                        dp[x][fa][k][0] = Min(dp[x][fa][k][0],dp[v][fa][l][0] + dp[x][fa][k - l][0]);
                        dp[x][fa][k][1] = Min(dp[x][fa][k][1],dp[v][x][l][0] + dp[x][fa][k - l][1]);    
                    }
                }
            }
        }
    }
    for(i = 1;i <= tot;i++)
    {
        int fa = ancestor[i];
        for(j = maxk;j >= 0;j--)
        {
            if(j >= 1)
                dp[x][fa][j][0] = Min(dp[x][fa][j][0] + wood[x] * (dep[x] - dep[fa]),dp[x][fa][j - 1][1]);
            else dp[x][fa][j][0] += wood[x] * (dep[x] - dep[fa]);
        }
    }
    tot--;
}
int main()
{
    int n,i;
    Read(n),Read(maxk);
    for(i = 1;i <= n;i++)
    {
        int v,dis;
        Read(wood[i]),Read(v),Read(dis);
        G[i].push_back(v);
        G[v].push_back(i);
        Road[i].push_back(dis);
        Road[v].push_back(dis);
    }
    vis[0] = 1;
    dfs(0);
    printf("%d",dp[0][0][maxk][0]);
    return 0;
}

おすすめ

転載: www.cnblogs.com/resftlmuttmotw/p/11323271.html