邮票问题 回溯法

邮票问题

设有已知面额的邮票m种,每种有n张,问用总数不超过n张的邮票进行组合,能组合的邮票面额中可以连续的面额数最大到多少?
例如:n=4 m=3
v1=1 v2=2 v3=4
最后的解为14。
请设计回溯算法求解以上问题,分析算法的时间复杂度;编码实现,要求结果正确。

问题分析

对给出的不同面值的邮票进行组合,且面值需组合成连续的数,就要求面值的总和从1开始,增量为1,一直增加到不连续为止。取一个结构体,c.value表示每个面值,c.choice表示面值的标号。这个问题的解向量为数组c.value,解向量的取值范围为不同的邮票面值,约束条件为邮票面值的总和需要是连续的。为了保证求得的总面额是连续的,需要在两个while循环之外再加一个for循环。for循环是为了保证求得的总面额数是连续的,每次for循环得到的数与上一个for循环得到的数作比较,若相等,此时得到的解为部分解,继续循环;若不等,此时的得到的解为全部解,继续循环得到的解释不再符合约束条件,则跳出循环,此时得到的面额总值就是所求。

算法设计

1.for k←1 to n k表示选了几张邮票
2.c[k].value←0; 解空间
3.c[k].choice←0; 表示有几种选择 即种类数 从1到m
4.end for
5.flag←false
6.for 当总面值不连续时退出循环
7.while k>=1 (k表示选择了几张)
8.while c[k].choice<m (每一种邮票面值都要选择)
9.c[k].choice←c[k].choice+1
10.j=c[k].choice
11.c[k].value←q[j]
12.tem←add(c,n) 将c中面值相加
13.if 如果面值数继续相等并且没到出口then flag←true并且跳出两个while循环
14.else if如果面值数不变并且没到出口 then k←k+1
15.end if
16.end while
17.c[k].choice←0
18.c[k].value←0
19.k←k-1{回溯}
20.end while
21.if flag then flag←false
22.for j←1 to k
23.c[j].value←0
24.c[j].choice←0
25.end for
26.k←1
27.end if
28.end for
29.return i-2

算法分析

在最坏的情况下会生成(n*((n(m+1))-1))/(n-1)个结点,对于每个生成的节点,都需要O(n)的工作来检查是否是合法解,或是部分解,或是非法解。因此在最坏时间下算法的全部运行时间为O(n(m+1))。空间复杂度为O(n^m)。

代码

#include <iostream>

using namespace std;

typedef struct stamp
{
    int value;//解空间 不同的邮票面额
    int choice;//表示有几种选择 即种类数 从1到m
}stamp;
int add(stamp c[],int n)//计算现有邮票总面值
{
    int tem=0;
    int i;
    for(i=1;i<=n;i++)
    {
        tem+=c[i].value;
    }
    return tem;
}
int maxvalue(int q[],int m,int n)
{
    stamp c[n+1];
    int k,j;
    bool flag;
    for(k=1;k<=n;k++)//k表示选了几张邮票
    {
        c[k].value=0;
        c[k].choice=0;
    }
    k=1;
    int i=1;
    flag=false;
    int tem=0;
    for(i = 1;tem==i-1;i++)//tem 表示总面值数 当不连续时退出循环
    {
        while(k>=1)//k表示选择了几张
        {
            while(c[k].choice<m)//每一种邮票面值都要选择
            {
                c[k].choice=c[k].choice+1;
                j=c[k].choice;
                c[k].value=q[j];
                tem=add(c,n);
                    //cout<<endl<<c[1].value<<' '<<c[2].value<<' '<<c[3].value<<' '<<c[4].value<<' ';注释部分帮助读者理解程序运行过程 输出结果为:第一张邮票取值 第二张邮票取值 第三张邮票取值 第四张邮票取值 现总面值 下一步要选择的运行路径
                    //if(tem == i)cout<<tem<<' '<<"goto Lab"<<endl;
                    //else cout<<tem<<' '<<"k++"<<endl;
                if(tem==i&&k<=n)//如果面值数继续相等
                {
                    flag=true;
                    goto Lab;
                }
                else if(tem<i&&k<n)//如果面值数不变
                {
                    k=k+1;
                }
            }
            c[k].choice=0;//回溯
            c[k].value=0;
            k=k-1;
        }
        Lab:
                if(flag)
                {
                    flag=false;
                    for(j=1;j<=k;j++)
                    {
                        c[j].value=0;
                        c[j].choice=0;
                    }
                    k=1;
                }
        }
        return i-2;
}
int main()
{
    int m,n,i;//m 种类 n 数量
    int MAX;
    cout<<"邮票种类数:";
    cin>>m;
    cout<<"每种张数:";
    cin>>n;
    int q[m+1];
    cout<<"由小到大输入各个面值:";
    for(i=1;i<=m;i++)
    {
        cin>>q[i];
    }
    MAX=maxvalue(q,m,n);
    cout<<"结果:"<<MAX;
    return 0;
}

运行结果

在这里插入图片描述

运行过程

在这里插入图片描述

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一个连续邮资问题回溯法 C++ 实现。连续邮资问题是一个经典的组合优化问题,其目标是找到一组邮票的面值,使得这些邮票可以组合成从 1 到给定的邮资数的任意邮资。 下面是一个简单的回溯法实现: ```cpp #include <iostream> #include <vector> using namespace std; void dfs(vector<int>& stamps, vector<int>& result, vector<int>& curr, int target, int sum, int start) { if (sum == target) { if (result.empty() || curr.size() < result.size()) { result = curr; } return; } else if (sum > target) { return; } for (int i = start; i < stamps.size(); i++) { curr.push_back(stamps[i]); dfs(stamps, result, curr, target, sum + stamps[i], i); curr.pop_back(); } } int main() { vector<int> stamps = {1, 2, 3, 4, 5, 6, 8, 10}; int target = 15; vector<int> result; vector<int> curr; dfs(stamps, result, curr, target, 0, 0); if (result.empty()) { cout << "No solution found." << endl; } else { cout << "The minimum number of stamps needed is " << result.size() << ":" << endl; for (int i = 0; i < result.size(); i++) { cout << result[i] << " "; } cout << endl; } return 0; } ``` 在上面的代码中,我们使用了一个 dfs 函数来搜索所有可能的邮票组合。我们传入的参数包括: - stamps:一个邮票面值的数组。 - result:一个存储最终结果的数组。 - curr:一个存储当前搜索路径的数组。 - target:目标邮资数。 - sum:当前邮票面值总和。 - start:开始搜索的邮票下标。 在 dfs 函数中,我们首先检查当前邮票面值总和是否等于目标邮资数。如果是,我们将当前搜索路径与最终结果进行比较,如果当前路径更短,则更新最终结果。如果当前邮票面值总和超过了目标邮资数,我们直接返回。 然后,我们遍历邮票数组,从 start 开始搜索。对于每个邮票面值,我们将其加入当前搜索路径中,然后递归搜索下一个邮票面值,直到找到一个解或者搜索完所有可能的邮票组合为止。 最后,我们输出最终结果,即邮票组合的数量和具体的邮票面值。 希望对您有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值