首页 >> 大全

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

2023-07-21 大全 30 作者:考证青年

回溯法的题目太多了,不想写这个代码了,于是我就开始水一篇文章,就单纯的分析一下这个问题保持整本书完整的队形

问题描述

如何用有限的邮票数,贴出更多面额的需求?

举例

n=5,m=4

设计1:X1={1, 3, 11, 15, 32}

连续邮资区间{1, 2, 3, ……, 70}

设计2:X2={1, 6, 10, 20, 30}

邮资连续区间{1, 2, 3, 4}

问题分析

定义x[1:n]表示n种不同邮票的面值,并约定他们从小到大排列,x[1]=1是唯一的选择,因为最大邮资区间必须从1开始。

接下来x[2]的取值范围是[2:m+1],因为第一张是1,最多可以贴m张

于是可以推测:对于已经选定的x[1:i-1],最大的连续邮资区间是[1:r],那么接下来x[i]的取值范围是[

+1 : r+1],若Xi>r+1, 则邮资r+1无法支付(约束条件)

在用回溯法解题目时,解空间树中各个节点的儿子是动态变化的,随着x的取值不同而变化

r 怎么计算呢? yi(j) 表示最多用 m 张面值 xi 的邮票贴出邮资 j 所需要的最少邮票张数

连续数集是什么意思__连续数集

计算X[1:i]的最大连续邮资区间在本算法中被频繁使用到,因此势必要找到一个高效的方法。

考虑到直接递归的求解复杂度太高,我们不妨尝试计算用不超过m张面值为x[1:i]的邮票贴出邮资k所需的最少邮票数y[k]。通过y[k]可以很快推出r的值。

//x[i-2]*(m-1)是第i-2层循环的一个上限,目的是找到r-1的值 
for (int j=0; j<= x[i-2]*(m-1);j++)         if (y[j]数组从而找到r 
while (y[r]

回溯函数

定义一些必要的变量

伪代码

代码(我抄的)

#include 
#define maxl 1000 //表示最大连续值
#define maxint 32767
int n, m;       // n为邮票种类数,m为能贴的最大张数
int maxvalue;   //表示最大连续值
int bestx[100]; //表示最优解
int y[maxl];    // y[k],存储表示到k值,所使用的最少邮票数
int x[100];     //存储当前解
void backtrace(int i, int r);int main(){printf("请输入邮票面值数:");scanf("%d", &n);printf("请输入能张贴邮票的最大张数:");scanf("%d", &m);for (int i = 0; i <= n; i++){x[i] = 0;bestx[i] = 0;}for (int i = 0; i < maxl; i++)  y[i] = maxint;x[1] = 1;y[0] = 0;maxvalue = 0;backtrace(1, 0);printf("当前最优解为:");for (int i = 1; i <= n; i++)  printf("%d ", bestx[i]);printf("\n最大连续邮资为:");printf("%d", maxvalue);return 1;
}void backtrace(int i, int r){//对上一层的邮资值数组进行更新,上限是x[i-1]*mfor (int j = 0; j <= x[i - 1] * m; j++){if (y[j] < m){//从只使用一个x[i]到使用m-y[i]个,即使用最多的最大值,降低邮票数//k是对表示j剩余的票数进行检查  for (int k = 1; k <= m - y[j]; k++){if (y[j] + k < y[j + x[i] * k]){//如果前面的某一个情况加上k个x[i],所达到邮资值使用的邮票数少于原来的邮票数则更新y[j + x[i] * k] = y[j] + k;}}}}//向后寻找最大邮资值,查看邮资范围扩大多少,然后查询y数组从而找到r while (y[r] < maxint){r++; //计算X[1:i]的最大连续邮资区间}if (i == n){ // i=n表示到达叶子节点。if (r - 1 > maxvalue){ //若大于最大值,则更新最优值与最优解for (int k = 1; k <= n; k++){bestx[k] = x[k];}maxvalue = r - 1;}return;}int z[maxl];//由于每一层需要对多种情况进行运算,因此需要将上一层的邮资值数组保留for (int k = 0; k < maxl; k++){z[k] = y[k];}for (int j = x[i] + 1; j <= r; j++){ //对下一层进行运算x[i + 1] = j;backtrace(i + 1, r - 1);for (int k = 0; k < maxl; k++)y[k] = z[k];}
}

输入:

2,3

5,4

4,3

输出

_连续数集_连续数集是什么意思

附一些数据

n= 5 m=4 {1,3,11,15,32} 最大邮资为70

n=5 m=5 {1,4,9,31,51} 最大邮资为126

n=4 m=2 {1,3,5,6}最大邮资为12

n=3 m=4 {1,5,8}最大邮资为26

n=6 m=4{1,4,9,16,38,49}最大邮资为108

n=5 m=6{1,7,12,43,52}最大邮资为216

可以结束了-但是-他还可以用动态规划来解决

设不超过m张面值为x[1:i]的邮票贴出邮资j所需的最少邮票数为y[j]。通过y[j]可以很快推出r的值。事实上,y[j]可以通过递推在O(n)时间内解决:yi(j) 表示最多用 m 张面值 xi 的邮票贴出邮资 j 所需要的最少邮票张数

类似于0-1背包问题:

dfs(x,cur,max)其中x数组存储当前的解有哪些(如果你就是求具体数值,而不要求解的组成甚至可以省略),cur代表目前到第几种面值,当到n为止,max表示到目前为止的最大可到达邮资,而之后下一个x[cur+1]一定是取x[cur]+1~max+1,这个很好理解,max+1目前是访问不到的,那么我加入x[cur+1]后,要看看max更新到多少,而max是从max+1到

m*x[cur+1](最多就是m个最大的那个邮票,最少肯定是要更新,不然要你何用...),接下来的问题就是能否用n种邮票,面值都储存在x数组中,最多贴m张,表示出某个数,

方法是动态规划,状态转移方程dp[i,j]=min(dp[i-1][j-k*a[i]+k),其实有点像背包;(注意边界!!!!!!!)dp[i][0]=0(没有钱时0张邮票)dp[1][i]=i;(因为第一张邮票就是1,贴几块钱就是几张邮票)

#include 
#include 
int n,m;//n为邮票种类,m为一封信上最多贴的邮票个数
int Max;
int ans[10000];//最终答案数组
int min(int a,int b)
{return am)return 0;return 1;}
void DFS(int x[10000],int cur,int max)
{int i,j,next;if (cur==n)//如果已经得出了n种邮票{if (max>Max)//并且它的最大值已经大于当前最大邮资数{Max=max;for (i=1;i<=cur;i++)ans[i]=x[i];//更新答案数组}return;}for (next=x[cur]+1;next<=max+1;next++)//如果还没得到n中邮票,那么从x[cur]+1~max+1选一个作为下一个邮资,因为max+1没法表示,所以必定到max+1为止{x[cur+1]=next;//接下来是重点,用种类为cur+1,数目分别为x[1..cur+1]的邮票,最多使用m张,能否表示出大于max的某个数for (i=max+1;i<=m*x[cur+1];i++)//这个数最少要为max+1(不然没有意义了),最多是x[cur+1]*mif (panduan(x,cur+1,i)==0)//如果成立break;if (i>max+1)//如果至少让最大值更新了一次DFS(x,cur+1,i-1);}}int main(){int i,j,max,cur;int x[1000];//中间传递的数组,存储当前的邮票值的解scanf("%d%d",&n,&m);Max=0;max=m;cur=1;x[cur]=1;DFS(x,cur,max);//x存储当前的解,cur表示当前传递到第几种邮票,max表示目前能表示到的最大值printf("%d\n",Max);for (i=1;i<=n;i++)printf("%d ",ans[i]);return 0;}

嘻嘻,抄完了,这个其实我没怎么搞懂,仅仅是大致搞明白了,特别是算r那个地方我不懂。

连续邮资问题详解_羊书的博客-CSDN博客_连续邮资问题

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了