邮政部门发行的邮票共有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;
}