DAG上的动态规划
在讲述DGA前我们先来初步了解下动态规划的基础和核心。
我们从数字三角开始:
现在要求我们从顶点开始一直到底层的数字相加和最大。(每层选一个数)。我们可能会想,从每层中选出最大的不就好了吗?但结果,很显然出现了问题,就比如1~n-1层的数都比较小,突然在n层的时候出现一个特别大的数,前面取大数的结果就要全部推翻。又有人会想到,那就贪心吗?不行就回溯,直到找到最大的那个值,但你在想一下就会发现,一个n层的数字三角形完整的路线有2n-1,当n很大时,回溯法的速度太慢。但你有没有想到,让每一个数的位置上都存的是从这个点到n层的最大值呢?比如d[i]表示从i到n的最大距离。这样这个点就可以看成一个状态量,显示的是i层到n层的一种状态。有了这层关系,我们就可以得到它的递归公式:d(i,j)=本身的值+max(d(i+1,j),d(i+1,j+1));
经此你应该对什么是动态规划有了初步的了解,下面我们就来稍稍总结和继续加深:
动态规划的特点和性质:
- 动态规划的核心就是状态和状态转移公式
- 存在重叠子影响运算效率(记忆化搜素和递推可以解决)
- 初始化尽量只用0和-1
重叠子解决方法一:递推
重叠子解决方法二: 记忆化搜素
现在我们就来将DAG了!
DAG是解决最短路,最长路或路径计数问题的一种常用方法,而在动态规划问题中很多的问题可以很巧妙的转化的DAG的问题上来。在DAG中也存在着两种存有区分的解决问题的模板。一种是任意起点和任意终点,另一种是指定起点和指定终点。好在两者互通性很强,理解起来也就没那么困难。
任意起点的最长路和字典序:
固定起点的最长路和最短路 ;
先给出代码:
int dp(int s)
{
if (vis[s])
return d[s];//计算过的不用再计算了
vis[s] = 1;
int &ans = d[s];
for (int i = 1; i <= n; i++)
if (s >= v[i])
ans = max(ans, dp(s - v[i]) + 1);//转移方程
return ans;
}
这里的固定也就是说,只能从开始的位置一直查到底,不能向两边同时查找。dp和vis,v数组的初始化,用memset多可以解决。出来这种方法外,我们还可以用地推的形式来处理
for(int i=1;i<=n;i++)
for (int j = v[i]; j <=ans; j++)//正序遍历
{
d[j] = max(d[j], d[j - v[i]] + 1);
}