数据结构&算法之动态规划(深度优先遍历)

最近在看关于数据结构系列知识点,然后遇到一个动态规划相关的题目——邮票规划。
首先先介绍下关于DPS,也就是深度优先遍历算法吧。

深度优先遍历

深度优先遍历,从初始访问结点出发,我们知道初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点。总结起来可以这样说:每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。

一句话的事情:优先进行纵向访问,而不是广度优先遍历的横向进行访问

动态规划相关知识点

动态规划算法的基本思想是:将带求解的问题分解成若干个相互联系的子问题,先求解子问题,然后从这些子问题的解中得到原问题的解;对于重复出现的子问题,只在第一次遇到的时候对它进行求解,并把答案保存起来,避免重复求解。该思想与记忆化搜索类似,即将计算步骤中的过程保存下来,避免重复运算

要点

  • 动态规划适用于这些子问题不是独立的情况,也就是各子问题包含公共子问题
  • 需要记录每个子问题求解的问题结果,以供其他问题进行解决

相关步骤

  1. 划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
  2. 确定状态和状态变量:注意状态必须满足无后效性。
  3. 确定决策:找到子问题是进行动态规划的重要一步。动态规划和递推更多应考虑本问题由哪些已解决子问题构成,而不是考虑本问题对将来哪些问题有贡献。
  4. 确定边界条件,写出状态转移方程

具体事例

问题描述
  给定一个信封,最多只允许粘贴N张邮票,计算在给定K(N+K≤13)种邮票的情况下(假定所有的邮票数量都足够),如何设计邮票的面值,能得到最大值MAX,使在1~MAX之间的每一个邮资值都能得到。

例如,N=3,K=2,如果面值分别为1分、4分,则在1分~6分之间的每一个邮资值都能得到(当然还有8分、9分和12分);如果面值分别为1分、3分,则在1分~7分之间的每一个邮资值都能得到。可以验证当N=3,K=2时,7分就是可以得到的连续的邮资最大值,所以MAX=7,面值分别为1分、3分。
输入格式
  一行,两个数N、K
输出格式
  两行,第一行升序输出设计的邮票面值,第二行输出“MAX=xx”(不含引号),其中xx为所求的能得到的连续邮资最大值。
样例输入
3 2
样例输出
1 3
MAX=7

当看到这个问题我们藉由之前的理论来具体分析下。

动态规划
  • 子问题
    • 是否选择面值各大的邮票呢,这里显示选择只能“是”或者“否”
  • 最优子结构
    • 当然同理,连续邮票的面值在每个值下都成立,这样就构成了结构。
  • 边界
    邮票的种类数目

当然有了上面的几点,我们试着写下该方式的转化方程,假如n代表邮票总共类别,i代表邮票总面值,stamp[i]代表邮票所属值大小,dp[i]代表凑齐 i 面值最少需要多少邮票,Dp(int position)代表几种类别的所能组成邮票值。

  • 当i >= stamp[k] && dp[i] > dp[i – stamp[k]] + 1,则 dp[i] = dp[i – stamp[k]] + 1;就是说取最大的那个。
    • 当dp[i]>n,Dp(position)=i,
    • 当dp[i]<n,Dp(postion)=-1;就是说无意义
DPS的使用
  • deep深度——由于每个邮件的类别数目难以确定,所以需要以邮票数目为DPS的递归深度deep)
  • 中止条件——当确定最大连续能取得值maxm,那么新加入的物品价值一旦大于maxm+1,显然就会出现断层,所以可以以maxm+1为枚举上界,然后这样进行下一层的dfs。

附上java代码:

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    static int N=500;
    static  int n,m,ans;
    static  int a[]=new int[N+1];
    static  int b[]=new int[N+1];
    static  int f[]=new int[N+1];
    public static void main(String[] args) {
        int i,j,k;
        Scanner scanner =new Scanner(System.in);
        n=scanner.nextInt();
        m=scanner.nextInt();
        a[1]=1;
        dfs(1);
        for(i=1;i<=m;i++)
            System.out.printf("%d ",b[i]);
        System.out.printf("\nMAX=%d\n",ans);
    }
    static void dfs(int deep)
    {
        int i,j,uplimit;
        Arrays.fill(f,0x3f);
        f[0]=0;
        for(uplimit=1;uplimit<=N;uplimit++)
        {
            for(i=1;i<=deep&&a[i]<=uplimit;i++)
                f[uplimit]=Math.min(f[uplimit],f[uplimit-a[i]]+1);
            if(f[uplimit]>n)
            {
                uplimit--;
                if(uplimit>ans)
                {
                    ans=uplimit;
                    for(i=1;i<=deep;i++)
                        b[i]=a[i];
                }
                break;
            }
        }
        if(deep==m)return ;
        for(i=uplimit+1;i>a[deep];i--)
        {
            a[deep+1]=i;
            dfs(deep+1);
        }
    }
}
    原文作者:CatherYan
    原文地址: https://www.jianshu.com/p/6a2d257e68d6
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞