邮票组合系统设计

邮政部门发行的邮票共有n种,存在整数r,使得用不超过m枚此种邮票可以组合出1-r的所有面值,但不能组合出面值r+1.例如,对于面值1、4、7、8,最多用三张邮票可以组合出1-24的面值,但不能组合出面值为25的邮票。对于给定的n、m及n枚邮票的面值,求r。

原始代码
采用枚举的方法,将给定参数所能够组合的所有邮票面值都求出来,然后进行排序,找到第一个不连续的地方,最后一个连续的值就是要求的最大连续面值。

#include "iostream"
#include "iomanip"
#define STAMP_KIND 4
#define TAKEOUT_NUM 3

using namespace std;

int main(void)
{
	int s_value[STAMP_KIND] = { 1,2,5,10 };
//	int s_value[STAMP_KIND] = { 1,3,7,12 };
	int combine_value[10000];
	int combine_num = 0;
	int i[STAMP_KIND] = { 0 };
	int value_sum = 0;
	int sum_i = 0;
	int value_i = 0;
	int exist_flag = 0;
	int sort_i, sort_j = 0;

	cout << "产生的面值:" << endl;
	for (i[0] = 0; i[0] <= TAKEOUT_NUM; i[0]++) {
		for (i[1] = 0; i[1] <= TAKEOUT_NUM; i[1]++) {
			for (i[2] = 0; i[2] <= TAKEOUT_NUM; i[2]++) {
				for (i[3] = 0; i[3] <= TAKEOUT_NUM; i[3]++) {
					if (i[0] + i[1] + i[2] + i[3] > TAKEOUT_NUM || i[0] + i[1] + i[2] + i[3] == 0) {
						continue;
					}
					else 
					{
						value_sum = 0;
						for (sum_i = 0; sum_i < STAMP_KIND; sum_i++)
						{
							value_sum += i[sum_i] * s_value[sum_i];
							//cout << "第几次" <<sum_i << " 个数" << i[sum_i] << " 值" << s_value[sum_i] << " 和" << value_sum<< endl;
							//cout << "-" << i[sum_i] << "*" << s_value[sum_i] ;
						}
						//cout<< endl;
						//cout << value_sum <<" "<< i[0] + i[1] + i[2] + i[3]<< endl;
						cout << value_sum <<" ";
						exist_flag = 0;
						for (value_i = 0; value_i < combine_num; value_i++) {
							if (combine_value[value_i] == value_sum)
							{
								exist_flag = 1;
							}
						}
						if (exist_flag == 0)
						{
							combine_value[combine_num] = value_sum;
							//cout << "kk"<< combine_num<<" "<<combine_value[combine_num] << endl;
							combine_num++;							
						}
					}
				}
			}
		}
	}
	cout << endl;

	cout << "去重后的面值:" << endl;
	for (value_i = 0; value_i < combine_num; value_i++) {
		//cout << value_i << " " << combine_value[value_i] << " " << endl;
		if (value_i != 0 && value_i != combine_num - 1 && value_i % 10 == 0)
			cout << endl;
		cout << setw(4) << combine_value[value_i] << " ";
		
	}
	cout << endl;

	cout << "对面值进行排序:" << endl;
	for (sort_i = 0; sort_i < combine_num-1; sort_i++) {
		for (sort_j = sort_i + 1; sort_j < combine_num; sort_j++) {
			if (combine_value[sort_i] > combine_value[sort_j])
			{
				int temp = combine_value[sort_i];
				combine_value[sort_i] = combine_value[sort_j];
				combine_value[sort_j] = temp;
			}
		}
	}
	for (value_i = 0; value_i < combine_num; value_i++) {
		if (value_i != 0 && value_i != combine_num - 1 && value_i % 10 == 0)
			cout << endl;
		cout << setw(4)<<combine_value[value_i] << " ";
		
	}
	cout << endl;

	cout << combine_num <<endl;
	//int a[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21 };
	//cout << "可以组合的最大连续面值为:" << endl;
	//for (value_i = 0; value_i < combine_num; value_i++) {
	//	if (value_i + 1 < combine_num && a[value_i + 1] != a[value_i] + 1)
	//		break;
	//}
	//if (value_i == combine_num)
	//	value_i--;
	cout << value_i << endl;
	//cout << a[value_i] << endl;

	cout << "可以组合的最大连续面值为:"<<endl;
	//此处使用value_i < combine_num-1避免最后一对比较产生越界现象
	for (value_i = 0; value_i < combine_num-1; value_i++) {
		//if (value_i + 1< combine_num && combine_value[value_i+1] != combine_value[value_i]+1)
		if (combine_value[value_i + 1] != combine_value[value_i] + 1) {
			break;
		}
		//当连续的值在最后一个位子跳出循环
		if (value_i == combine_num-1)
			break;
	}
	cout << combine_value[value_i]<<endl;
}

这种方法具有很大的局限性,在于,邮票的种类数目必须提前给定好,像上述代码的邮票种类数固定为4种,只能适用于单一情况,当情况发生改变时,代码必须跟随改变,不具有一般性。

优化后代码
采取动态规划的方法,实质上就是一个完全背包问题。注意转移方程的列写方式。
StampCnt[i] = min(StampCnt[i], StampCnt[i-Stamp[j]] + 1);
StampCnt[i] 表示得到邮资i所用的邮票数的最小值
stamp为邮票集合

#include <iostream>
#include <string.h>
 
using namespace std;

#define MAX 10
#define MAXINT 100000
#define min(a, b) ((a) >= (b) ? (b) : (a))

int Stamp[MAX+1];
int StampCnt[MAXINT] = {0};

//dp(i)表示得到邮资i所用的邮票数的最小值,初始条件为dp[0]=0, dp[stamp[i]]=1,
//stamp为邮票集合。则dp[i] = min{dp[i],dp[i-stamp[j]]+1}

int MaxValue(int n, int m)
{
    int i, j;

    for(i = 1; ; i++)
    {
        StampCnt[i] = 999;
        for(j = 1; j <= m; j++)
        {
        	//cout<<"外循环 "<<i<<"   内循环 "<<j<<endl;
			//如果当前面值正好与所需面值相同,即仅需要就可以凑出
            if(Stamp[j] == i)
            {
            	//cout<<"if "<<i<<endl;
                StampCnt[i] = 1;
                break;
            }
			//如果当前面值小于所需面值
            else if(i > Stamp[j])
            {
            	//i-Stamp[j]所需面值与当前值得的差值
                StampCnt[i] = min(StampCnt[i], StampCnt[i-Stamp[j]] + 1);
                //cout<<"得到  "<<i<<" 邮票数最小值:"<<StampCnt[i]<<endl; 
            }
        }
        if(StampCnt[i] > n)
            return i-1;
    }
  //  return 0;
}

int main()
{
    int N, M;
    int i, j;

    cin >> N >> M;  //输入 3 5 0 1 2 5 10, 输出 17

    memset(Stamp, 0, sizeof(Stamp));
    for(i = 1; i <= M; i++)
    {
        cin >> Stamp[i];
    }

    cout << MaxValue(N, M) << endl;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值