算法设计与分析实验 (连续邮资问题)

1、实验环境

     VisualC++ 6.0

2、 实验目的和要求

l 实验目标:利用回溯法求解连续邮资问题。

l 实验内容:

Ø 假设国家发行了n种不同面值的邮票,并且规定每张信封上最多只允许贴m张邮票。

l 实验要求:

Ø 连续邮资问题要求对于给定的n和m的值,给出邮票面值的最佳设计,在1张信封上可贴出从邮资1开始,增量为1的最大连续邮资区间。

l 当n=2、m=3时:

Ø 如果面值分别为1、4,则在1-6之间的每一个邮资都能得到(当然还有8、9和12);

Ø 如果面值分别为1、3,则在1-7之间的每一个邮资都能得到;

l 当n=5、m=4时:

Ø 面值为(1,3,11,15,32)的五种邮票可以贴出邮资的最大邮资区间是1到70.

3、解题思路、伪代码
3.1解题思路
(1).先从简单入手,若前cur张邮票面值已经确定,如何判断由这些面值,在张数不超过m的情况下能否贴出该邮资re。很容易想到,这是一个类似背包问题,可采用用回溯法求解。

checkanser(temp,cur,re,m,num)

其中:

temp数组:记录已经确定下来的面值

cur:当前已经确定的面值数量

re:当前需要贴出的邮资

m:信封最多允许贴m张邮票

num:已经张贴的邮票数量

1向信封上贴第一张邮票,可以有cur种选择,当选择了第i张面值时,问题缩小为:

前cur张邮票面值已经确定,如何判断由这些面值,在张数不超过m的情况下能否贴出该邮资re-temp[i].

即:checkanser(temp,cur,re-temp[j],m,num+1)

2当re=0时,算法结束,表示由已确定下来的邮票,能够在满足条件的情况下,贴出邮资为re.

3当re=1时,表示当前贴法无法贴出邮资re,则选择贴下一张邮票,若所有的邮票都不满足要求,向前回溯。

4约束条件:已经张贴的邮票数量num不得超过m

(2).判断由已选的面值,在张数不超过m的情况下能贴出的最大邮资

即从小能到大依次连续枚举邮资,调用(1)判断该邮资能否取得,从而得出最大连续邮资。

(3).已经选取到第cur种面值邮票,确定下一张面值

若前cur张面值为temp[cur],最大能贴出的最大连续邮资为max,显然下一张面值的可能取值只可能在temp[cur]+1到

max+1之中。由于前面的邮票的面值会影响后面面值能,如n=3,m=4时,最优面值为(1,5,8);n=4,m=4时,最优面值为:(1,3,11,18),因此,该问题不具有无后向性,不是一个贪心问题。于是采用dfs搜索算法进行求解,dfs树的每一个叶子代表可能的面值,每一条从根到叶子的路径表示一组满足条件的解。最后从所有满足条件的解中选出最优解。



代码

#include <stdio.h>

#include <string.h>

int n;//n种面值

int m;//最多允许贴m张

int result[100];//最终结果

int temp[100];//暂存各种可能结果

int maxRe=0;//最大邮资

int checkanser(int temp[],int
cur,int re,int m,int num);

void getNext(int temp[],int
cur,int max,int m);

int maxl(int temp[],int
cur,int m,int max);

//判断由已选的面值,在张数不超过m的情况下能否贴出该邮资re

//回溯法判断

int checkanser(int temp[],int
cur,int re,int m,int num)//cur:当前已经确定的面值数量num:已经张贴的邮票数量

{   

    if(re==0)

        return 1;

    if(re<0)

        return 0;

    for(int j=cur;j>=1;j--)

    {

        if(num<m)

        {

            num++;

            if(checkanser(temp,cur,re-temp[j],m,num)==1)//使用了第i个面值的邮票

            {

                return 1;

            }else

            {

                num--;//恢复现场

            }

        }

    }

    return 0;

}

//判断由已选的面值,在张数不超过m的情况下能贴出的最大邮资

int maxl(int temp[],int
cur,int m,int max)

{

    for(int k=max+1;k<m*temp[cur];k++)

    {

        if(checkanser(temp,cur,k,m,0)==0)

            break;

    }

    return k-1;

}

void getNext(int temp[],int
cur,int max,int m)//cur:已经选取到第cur种邮票,确定下一张面值

{

    if(cur==n)//遍历到叶子

    {

        if(maxRe<max)

        {

            maxRe=max;

            for(int z=1;z<=n;z++)

            {

                result[z]=temp[z];

            }

        }

        return;

    }

    for(int k=temp[cur]+1;k<=max+1;k++)

    {

        temp[cur+1]=k;

        int max1=maxl(temp,cur+1,m,max);//下一个面值取k时能达到的最大邮资

        if(max1>max)//k可取

        {

            getNext(temp,cur+1,max1,m);//继续递归确定接下来的面值

        }

    }

}

int main()

{

    printf("请输入面值数量(n):\n");

    scanf("%d",&n);

    printf("请输入每张信封最多允许贴的邮票数量(m):\n");

    scanf("%d",&m);

    temp[1]=1;

    getNext(temp,1,m,m);

    printf("\n邮票面值分别为:");

    for(int i=1;i<=n;i++)

    {

        printf("%d ",result[i]);

    }

    printf("\n最大邮资区间为:1-%d\n",maxRe);

    return 0;

}


4、实验步骤
4.1输入
在这里插入图片描述
4.2输出:
在这里插入图片描述

5、讨论和分析
对算法及实验结果进行分析讨论。
实验结果分析:
经验证当输入n=5,m=4情况下
最大邮资区间为1-70
验证成立。
总结:
○1当遇到算法问题时,要先判断它属于什么类型的问题。由于前面的邮票的面值会影响后面面值能,如n=3,m=4时,最优面值为(1,5,8);n=4,m=4时,最优面值为:(1,3,11,18),因此,该问题不具有无后向性,不是一个贪心问题。
○2使用回溯法时要注意恢复现场,确定边界条件和回溯的条件,才能通过回溯得到正确的结果。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值