Riv——树形DP

题目

题目链接

思路

整道题看起来有很多要点要考虑

最先想出的DP方程

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

看起来挺好的,时间复杂度也不高,但问题来了

cost怎么算啊

不知道剩余木材 不知道走的距离

于是老师说 $\color{red}\text{要三维}$

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

然后我 $\color{blue}\text{没有想出来}$

于是去看了洛谷题解,但觉得讲的不是很清楚,特于此再写一篇

定义四维的

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