DAG上的动态规划
当题目中的各个状态之间的关系是连续无环图的时候(树的)关系,这个时候就可以考虑下树上的动态规划,因为点与点之间的关系是树的关系,所以一个点的能和它有关系的只有儿子结点或者它的父亲结点。
这个时候我们要是要求一种最长的距离, 就可以将一个点看做两种方式:
- 这个点是的权值是以这个点为结尾的最长距离
- 这个点的权值是以这个点 i 为开始的所能到达的最长的距离,用其他点 j 来更新 i 点的值
第一种方法:
其实不难发现第一种方案在大部分的情况下是十分难实现的,因为要从n-1个点中挑选出几个点形成最长的距离很难,要把全部的点遍历一遍才知道。在经过和xy思维的碰撞后,本菜鸡成功领悟了一点点方法:拓扑排序来完成这个操作
拓扑排序听学长说是符合要求的一定要在这个点的前面,但是也同样允许不符合条件的在前面。
而正是因为这种排序不是面面俱到的排序,才让其拥有了包含一种内在关系的能力。而拓扑排序实现的过程就是用一个点的出度来实现。
一般我都是采取第二种方法:
-
首先如果题目中有明显的先后关系(明显的树的形状),那么我们就可以用数组dp来写状态转移方程:
举个栗子:有个背包的容量是V,要选体积是a,b,c的物品(他们价值是w1, w2, w3),在背包里面放尽可能多的价值。
我认为一种考虑可以是dp【i】表示体积已经有i所能够继续装的最大价值
在这里很显然就是第二种方法,以这个体积i为起点所能够装的最大价值,而且他们这里存在明显的先后关系:背包的容量, 那么我就可以把背包的容量做为树链,把每个体积为i的状态串起来。
很显然dp【V】 = 0,体积已经最大,我们不能再装下东西了,所以能继续装的最大价值是0。这样我们就可以倒着遍历从v->0,谱写好状态转移的方程式,我们就可以直接输出dp[0]. -
但是如果说这里存在先后关系,但是我们不确定哪个点一定在前,哪个点一定在后,那么这样子我们就可以记忆化搜索递归来求解。
例题
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 <