动态规划——DAG上的动态规划

博客探讨了在动态规划问题中,如何处理状态间呈树形关系的情况。文章通过uva437巴比伦塔问题举例,解释了两种方法:一是以点为终点的最长距离,可通过拓扑排序解决;二是以点为起点的最长距离,适用于有明显先后关系的问题,可用数组dp表示状态转移。对于没有明显顺序的题目,可采用记忆化搜索递归求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DAG上的动态规划

当题目中的各个状态之间的关系是连续无环图的时候(树的)关系,这个时候就可以考虑下树上的动态规划,因为点与点之间的关系是树的关系,所以一个点的能和它有关系的只有儿子结点或者它的父亲结点。
这个时候我们要是要求一种最长的距离, 就可以将一个点看做两种方式:

  1. 这个点是的权值是以这个点为结尾的最长距离
  2. 这个点的权值是以这个点 i 为开始的所能到达的最长的距离,用其他点 j 来更新 i 点的值

第一种方法:
其实不难发现第一种方案在大部分的情况下是十分难实现的,因为要从n-1个点中挑选出几个点形成最长的距离很难,要把全部的点遍历一遍才知道。在经过和xy思维的碰撞后,本菜鸡成功领悟了一点点方法:拓扑排序来完成这个操作

拓扑排序听学长说是符合要求的一定要在这个点的前面,但是也同样允许不符合条件的在前面。
而正是因为这种排序不是面面俱到的排序,才让其拥有了包含一种内在关系的能力。而拓扑排序实现的过程就是用一个点的出度来实现。

一般我都是采取第二种方法:

  1. 首先如果题目中有明显的先后关系(明显的树的形状),那么我们就可以用数组dp来写状态转移方程:
    举个栗子:有个背包的容量是V,要选体积是a,b,c的物品(他们价值是w1, w2, w3),在背包里面放尽可能多的价值。
    我认为一种考虑可以是dp【i】表示体积已经有i所能够继续装的最大价值
    在这里很显然就是第二种方法,以这个体积i为起点所能够装的最大价值,而且他们这里存在明显的先后关系:背包的容量, 那么我就可以把背包的容量做为树链,把每个体积为i的状态串起来。
    很显然dp【V】 = 0,体积已经最大,我们不能再装下东西了,所以能继续装的最大价值是0。这样我们就可以倒着遍历从v->0,谱写好状态转移的方程式,我们就可以直接输出dp[0].

  2. 但是如果说这里存在先后关系,但是我们不确定哪个点一定在前,哪个点一定在后,那么这样子我们就可以记忆化搜索递归来求解。

例题

uva437 巴比伦塔

传送门

题解:
这道题我们就可以把这些立方体的底面积看成树的关系(因为下面的长宽一定要比上面的都大)
因为我们不能明显的看出这些立方体哪个是在上,哪一个是下的,所以我们就采取第二种方法

#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int id[35][3];//编号为i的立方体的编号为j的边长长度
int max1[35][3];//max1【i】【j】以编号i的立方体的编号为j的边为高,所能叠出来的最高的高度 
int vis[35][3];
int ans = -1;
int n;
int cnt = 0;
int dp(int now, int g) //以now为底的那个,g(0~2)为高,返回最高高度 
{
   
	vis[now][g] = 1;
	int l, w;

	max1[now][g] = max(max1[now][g], id[now][g]);
//	cout << now << ' ' << g  << ' ' << id[now][g] << endl;
	if(g == 0)
	{
   
		l = id[now][1];
		w = id[now][2];	
	}	
	else if(g == 1)
	{
   
		l = id[now][0];
		w = id[now][2];				
	}
	else 
	{
   
		l = id[now][0];
		w = id[now][1];
	}
	
	for(int z = 0; z <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值