• Riv——树形DP


    题目

    题目链接

    思路

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

    最先想出的DP方程

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

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

    cost怎么算啊

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

    于是老师说 (color{pink}{要三维})

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

    然后我 (color{pink}{没有想出来})

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

    定义四维的

    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;
    }
    
  • 相关阅读:
    围棋角部大眼的气
    openwrt 增加自定义包
    OpenWrt – 编译前的./scripts/feeds脚本用法
    memset 赋初值为无穷大和无穷小
    android 自动点击学习课程
    最多硬币问题
    最少硬币问题
    9.2 嵌套矩形问题
    ajax请求到一个controller在跳转到下载的controller上也不能下载,百度了一下总结下原因:发现原来jQuery的ajax回调已经把response的数据傻瓜式的以字符串的方式解析.
    MySQL 数据库的远程访问权限
  • 原文地址:https://www.cnblogs.com/resftlmuttmotw/p/11323271.html
Copyright © 2020-2023  润新知