问题描述

假设国家发行了n种不同面值的邮票,并且规定每张信封上最多只允许m张邮票。连续邮资问题要求对于给定的n和m的值,给出邮票面值的最佳设计,即在1张信封上可贴出从邮资1开始,增量为1的最大连续邮资区间。例如,当n=5和m=4时,面值为(1,3,11,15,32)的5种邮票可以贴出邮资的最大连续邮资区间是1-70。

问题分析

对于连续邮资问题,用n元组x[1:n]表示n种不同的邮票面值,并约定它们从小到大排列。x[1]=1是惟一的选择。此时的最大连续邮资区间是[1:m]。x[2]的可取值范围是[2:m+1]。在一般情况下,已选定x[1:i-1],最大连续邮资区间是[1:r],则x[i]的可取值范围是[x[i-1]+1:r+1]。

解题思路

由上述的问题分析可知,该问题需要使用深度优先搜索,搜索的对象是每种邮票的面值,第i层对应的是对第i种邮票取值的搜索。从其子结点中找到构成最大连续面值的组合,并与本层结点的面值,共同构成本层结点的结果。
对于搜索过程中的状态,采用二维数组进行存储。C[i][j]表示前i种邮票,构成总面值j时,所需的最少张数,整个搜索的过程就是不断对二维数组进行更新的过程。每次搜索到第n层时,便比较当前邮票组合下的最大连续邮资区间的上限是否大于已搜到结果。若是,则记录该值及对应的邮票面值组合。
findMax()函数的设计
因为findMax()函数在每一个搜索结点中都要调用,所以该函数的效率将直接影响算法的复杂度。
这里为了尽可能少的进行元素的更新,采用了两个方向的DP:
向下DP:若C[i][1]到C[i][j]是前i种邮票构成对应面值所需的最少张数,则可以利用该数据,找到C[i+1][1]到C[i+1][j]对应取值。
向右DP:若C[i][1]到C[i][j]是前i种邮票构成对应面值所需的最少张数,则可以利用该数据,对数组向右进行动态规划,找到其连续邮资区间上限。

为了对该方法有个直观的理解,举例如下所示:
题目:邮票种类3种,邮票张数上限为3

1.第1种邮票的面值一定是1,因为第0种邮票不存在,所以在第一行对面值1的邮票做向右DP,更新的数据为下表中标为粗体的部分。

2.则第2种邮票的面值的取值范围为[2,4],这里先取其面值为2。因为前1种邮票在DP矩阵中已经存在总面值为1到3的数据,所以可以直接利用这些数据,做向下DP,更新的数据为下表中标为粗体的部分。


3.为了找出前2种邮票的最大邮资区间上限,利用前2种邮票在总面值为1到3的最少张数,做向右DP,这个过程只利用到当前行的数据,更新的数据为下表中标为粗体的部分。下表中,第二行第五列的,即前2种邮票构成总面值为4时的最少张数为2,是这样子计算出来的:若构成总面值为4时的最后一张邮票的面值为1,则需要的邮票的张数为“前2种邮票构成总面值为4-1=3时的最少张数2”加上1,即3张;若构成总面值为4时的最后一张邮票的面值为2,则需要的邮票的张数为“前2种邮票构成总面值为4-2=2时的最少张数1”加上1,即2张。则“前2种邮票构成总面值为4时的最少张数”为min{2,3}=2张。
显然,这样的表述过程过于啰嗦,不过为了方便理解,这是值得的。


4.则第3种邮票的面值的取值范围为[3,7],这里先取其面值为3。因为前2种邮票在DP矩阵中已经存在总面值为1到6的数据,所以可以直接利用这些数据,做向下DP,更新的数据为下表中标为粗体的部分。


5.为了找出前3种邮票的最大邮资区间上限,利用前3种邮票在总面值为1到6的最少张数,做向右DP,这个过程只利用到当前行的数据,更新的数据为下表中标为粗体的部分。此时取得了原问题的一个解,三种邮票面值为(1,2,3)时的连续邮资区间上限为9。


6.回溯法返回上一层结点,选择第3张邮票的下一面值为4。因为前2种邮票在DP矩阵中已经存在总面值为1到6的数据,所以可以直接利用这些数据,做向下DP,更新的数据为下表中标为粗体的部分。


7.为了找出前3种邮票的最大邮资区间上限,利用前3种邮票在总面值为1到6的最少张数,做向右DP,这个过程只利用到当前行的数据,更新的数据为下表中标为粗体的部分。此时取得了原问题的又一个解,三种邮票面值为(1,2,4)时的连续邮资区间上限为10,优于之前的解,则更新原问题的当前最优解。

8.不断重复上述过程,便可以找到原问题最优解。

一种可行的剪枝方法
记邮票种类为m,张数限制为n,使用回溯法不断更新的最大连续邮资区间上限为max。
则在遍历第m种邮票的面值时,若x[m]*n<=max便可以直接返回,因为当前邮票面值的组合中x[m]为最大值,不论连续与否,x[m]*n是当前邮票面值组合下的最大值,若这个值都比max要小,则其最大连续邮资区间上限也小于max。
这样可以省去一部分对最后一层结点的动态规划的过程。

package com.zyc.test;

import org.junit.Test;

import java.text.DateFormatSymbols;

public class MaxEms {

private static int knd = 5;//邮票种类
private static int lim = 4;//限制张数
private static int cnt = 0;//当前邮票种类
private static int max = 0;//存储邮资最大区间
private static int[] x = new int[100];//当前邮票面值
private static int[] r = new int[100];//最终的最优结果
private static int[][] C = new int [100][10000];//记录搜索结点状态public static void dfs(){if (cnt == knd) {//判断 当前 的邮票种类 是否等于 最大种类数 也就是已经获取全部的票if (x[cnt] * lim < max) //做一个限制 如果当前 所有面值 中的最大值 * 限制张数 都小于 max 那几个数 可定不是 最优解return;int tmp = findMax(); //算出 当前 所有面值 所获得的最大面区间的 给 maxif (tmp > max) {max = tmp;for (int i = 1; i <= knd; i++)  //把面值 赋值给  村结果的数组r[i] = x[i];}}else {int tmp = findMax(); //获取 当前 票面的 最大值// 下面用公式  :x[i]的可取值范围是[x[i-1]+1:r+1]for (int i = x[cnt] + 1; i <= tmp + 1; i++) {//下一层结点的面值的可能取值x[++cnt] = i;//将可能面值加入当前面值组合中dfs();cnt--; // 走完 这一趟 dfs  恢复下标}}
}public static int findMax(){ //计算  现有 x[] 中的最大邮资区间int j = 1;//向下DPwhile (C[cnt - 1][j] != 0) { //遍历表中上一行  为 下一行 赋值//判断 当前票面是否大于值 || 比较 当前点 的上方 和 当前点的 总面值 -减去当前面值 的差  的最优解 +1//C[cnt][j - x[cnt]] 代表 当前点的 总面值 - 减去当前面值 的差  的票数//C[cnt - 1][j]代表 无当前面值时 获得此时 总面值的 最票数if (j < x[cnt] || C[cnt - 1][j] <= C[cnt][j - x[cnt]] + 1)C[cnt][j] = C[cnt - 1][j];elseC[cnt][j] = C[cnt][j - x[cnt]] + 1;j++;}//向右DPwhile (true) {int tmp = Integer.MAX_VALUE;//赋值为 最大值  存放  邮票张数for (int i = 1; i <= cnt; i++) {if (tmp > C[cnt][j - x[i]] + 1)tmp = C[cnt][j - x[i]] + 1;C[cnt][j - x[cnt]] 代表 当前点的 总面值 - 减去当前面值 的差  的票数}if (tmp == Integer.MAX_VALUE || tmp > lim) //最限制  当temp等于最大值  或者 大于最大张数 结束break;elseC[cnt][j] = tmp;j++;}C[cnt][j] = 0;return j - 1;
}public static void main(String[] args) {x[1] = 1; //已知 第一个 面值  为  1cnt = 1;  //因为  已知  第一个面值为  1 所以 现在 种类数  为 1;dfs(); //调用深搜   跑 算法   走你..System.out.println(max);for (int i = 1; i <= knd; i++)System.out.print(r[i] + " ");System.out.println();
}

}

算法:连续邮资问题(回溯+动态规划+剪枝)相关推荐

  1. 连续邮资问题-回溯法

    回溯法的设计思想: 回溯法从根节点出发,按照深度优先策略遍历解空间树,搜索满足约束条件的解.在搜索至树中任一节点时,先判断该节点对应的部分解是否满足约束条件I,是否超出目标函数的界,也就是判断该节点是 ...

  2. N皇后的问题——算法的思想:回溯和剪枝

    package com.dym;public class Queens {public static void main(String[] args) {// TODO Auto-generated ...

  3. 回溯算法,模板,拔河,旅行商,连续邮资问题题解

    文章目录 回溯算法模板 递归回溯 迭代回溯 子集树 排列树 题目 拔河问题 旅行商问题 连续邮资问题 回溯算法模板 递归回溯 回溯法对解空间做深度有限搜索,因此在一般情况下可用递归函数来实现回溯法如下 ...

  4. 5.12回溯法--连续邮资问题--子集树

    回溯法的题目太多了,不想写这个代码了,于是我就开始水一篇文章,就单纯的分析一下这个问题保持整本书完整的队形 问题描述 如何用有限的邮票数,贴出更多面额的需求? 举例 n=5,m=4 设计1:X1={1 ...

  5. 算法——回溯与剪枝(Backtracking and pruning)

    算法之回溯与剪枝(Backtracking and pruning) 思想:将回溯法与分支限界法原理结合.应用访问,用剪枝来排除不满足解的情况来提高算法的执行效率 算法总结:回溯法的优点是可以遍历所有 ...

  6. 连续邮资问题的回溯法解决办法

    /* 连续邮资问题 算法设计: 该问题是设计最佳的邮票面值,用来表示最大的区间 对于连续邮姿问题,用n元组x[1:n]表示n种不同的邮票面值并约定它们从小到大排列. 整数r表示当前使用不超过m张邮票能 ...

  7. 回溯法求解连续邮资问题

    实验内容与要求 内容:假设某国家发行了n中不同面值的邮票,并且规定每张信封上最多只允许贴m张. 要求:对于给定的m和n的值,给出邮票面值的最佳设计,使得可以在一张信封上贴出从邮资1开始,增量为1的最大 ...

  8. 算法设计:连续邮资问题

    问题描述: 假设某国家发行了n种不同面值的邮票,并且规定每张信封上最多只允许贴m张邮票.连续邮资问题要求对于给定的n和m,给出邮票面值的最佳设计,在1张信封上贴出从邮资1开始,增量为1的最大连续邮资区 ...

  9. 算法 64式 8、动态规划算法整理_第1部分_1到15题

    1 算法思想 动态规划 1.1含义 把问题分解成多阶段或多个子问题,顺序求解各个子问题,最后一个子问题就是初始问题的解. 概念 阶段: 问题分成的顺序的几个环节.例如最长递增子序列中每个字符就是一个阶 ...

最新文章

  1. java类方法不可见_使java方法仅对特定类可见
  2. matlab中 text 使用画图示例
  3. 从门外汉到 Go 圈网红技术博主的五年历程
  4. 刚刚人均国民收入突破1万美元,作为打工人的你有感知吗?
  5. MyBatis映射文件3(参数处理Map)
  6. 3 个 Zabbix常见问题的处理方法
  7. 怎样成为PHP 方向的一个合格的架构师(转)
  8. delphi调用python_Delphi使用Python来解码邮件
  9. Web2.0创业者面临艰难选择:出售还是发展
  10. c语言prn文件,C语言prntf和scanf函数.doc
  11. 如何在IDEA中导出jar包
  12. mac开发php集成环境
  13. qcc512x_qcc302x笔记之环境搭建(一)
  14. Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day6】 —— 网络编程1
  15. Percent百分比布局用法
  16. 研究是一门艺术 前三章节笔记
  17. 【文档】Word如何将一个文档的样式复制到另一个文档中
  18. 【Java集合】集合是什么?为什么要用集合?
  19. 低代码在爱奇艺鹊桥数据同步平台的实践
  20. 解决Required XXX parameter ‘XXX‘ is not present问题

热门文章

  1. 从0到1搭建数仓DWD层案例实践
  2. 激光物理学取得突破,三大物理学家共获诺贝尔奖
  3. HDU - 2044一只小蜜蜂 一道递推题
  4. 实验六计算机网络通信Socket编程,计算机网络socket编程实验报告(3页)-原创力文档...
  5. 学习笔记-用PLAN法提高执行力
  6. 白门柳-刘斯奋有声小说
  7. Trustonic,Rubean和CCV宣布合作开发mPOS(移动销售终端)解决方案以实现商用设备上的密码安全输入
  8. 35亿美金押注链游,从VC投资看2022区块链风向标
  9. C++课程实训——银行系统
  10. 关于天龙八部3的感想