连续邮资问题
------策划方案
1、问题描述
假设国家发行了n种不同面值的邮票,并且规定每张信封上最多只允许贴m张邮票。连续邮资问题要求对于给定的n和m的值,给出邮票面值的最佳设计,在1张信封上可贴出从邮资1开始,增量为1的最大连续邮资区间。
2.解决方案及基本思想(递归)
(1)基本思路:
搜索所有可行解,当i<=n时,当前结点Z是解空间中的内部结点。在该结点处x[1:i-1]能贴出最大连续区间为r-1。因此,在结点Z处,x的可能取值范围是[x[i-1]+1:r],从而,结点Z有r-x[i-1]个儿子结点。算法对当前结点Z的每一个儿子结点以深度优先遍历的方式递归的对应子树进行搜索,从而找出最大连续邮资区间的方案。
(2)解向量:
用n元组x[1:n]表示n种不同的邮票面值,并约定它们从小到大排列。x[1]=1是唯一的选择。(x表第i张邮票的面值)
(3)可行性约束函数:
已选定x[1:i-1],最大连续邮资区间是1—r-1,下来x的可取值范围是x[i-1]+1—r。(r-1表x[1:i-1]所能达到的连续区间的上界)
(4)如何确定r的值:
计算x[1:i]的最大连续邮资区间在本算法中被频繁使用到,因此势必要找到一个高效的方法。考虑到直接递归的求解复杂度太高,用不超过m张面值为x[1:i]的邮票贴出邮资k所需的最少邮票数y[k]。通过y[k]可以很快推出r的值。具体算法如下:
for (j=0; j<= x[i-2]*(m-1);j++)
if(y[j]<m)
for (k=1;k<=m-y[j];k++)
if (y[j]+k<y[j+x[i-1]*k]) y[j+x[i-1]*k]=y[j]+k;
while (y[r]<MAXNUM) r++;
在初始化y[]是都把它赋值为MAXNUM(y[0]=0),其中MAXNUM表足够大的整数。
3.核心算法
在该算法核心的算法是如何求最大连续区间r-1的值
for (j=0; j<= x[i-2]*(m-1);j++)
if(y[j]<m)
for (k=1;k<=m-y[j];k++)
if (y[j]+k<y[j+x[i-1]*k]) y[j+x[i-1]*k]=y[j]+k;
while (y[r]<MAXNUM) r++;
在初始化y[]是都把它赋值为MAXNUM(y[0]=0),其中MAXNUM表足够大的整数。
4.初步模块
InitStamp(*S, nn, mm){
//给S赋初值。
//其中S为指向Stamp类型的指针变量,nn为邮票的种数,mm为最多贴邮票的张数。
}
BackTrack(*S,i,r){
//递归回溯,求出前i种邮票所能达到的最大连续区间1¬-r-1
}
OutPut(S){
//输出maxvalue和*bestx
}
main(){
//调用上述模块,解决连续邮资问题。
}
各模块之间的调用关系如下:
InitStamp(*S, nn, mm){
•••
BackTrack(*S,2,1);
•••
}
BackTrack(*S,i,r){
•••
BackTrack(*S,i+1,r+1);
•••
}
main(){
•••
InitStamp(*S, nn, mm);
OutPut(S);
}
5、数据结构
typedef struct{
int n, //邮票面值数
m, //贴邮票最多数
maxvalue; //当前最优解
int *bestx; //当前最优解
int *x; //当前解
int *y; //贴出各种邮资所需最少邮票数
}Stamp;
6、测试数据
当n=5和m=4时,面值为(1,3,11,15,32)的5种邮票可以贴出邮资的最大连续邮资区间是1到70。