入门题集----07----连续邮资问题*有bug

连续邮资问题

【问题描述】

G国发行了n种不同面值的邮票,并且规定每张信封上最多只允许贴m张邮票。连续邮资问题要求对于给定的n和m的值,给出邮票面值的最佳设计,使得可在1张信封上贴出从邮资1开始,增量为1的最大连续邮资区间。

例如,当n=5和m=4时,面值为(1,3,11,15,32)的5种邮票可以贴出邮资的最大连续邮资区间是1到70。

编程任务: 对于给定的正整数m和n,计算出邮票面值的最佳设计。

 

要求

数据输入】输入数据每一行给出2个正整数m和n的值(1<=n,m<=9),最后以0 0 表示文件结束。

     输入:

          2(邮票的种类数) 3(允许张贴的邮票数)

          2 3

          5 4

    输出:

          7

          1 3

           70

           1 3 11 15 32

/*

假设国家发行了n种不同面值的邮票,并且规定每张信封上最多只允许贴m张邮票。连续邮资问题要求对于给定的n和m的值,给出邮票面值的最佳设计,

在一张信封上可以贴出从邮资1开始,增量为1的最大连续邮资区间。

举例分析:

当n=2,m=3时,如果面值分别为1和4,则可以获得的邮资范围为1~6 加上 8 , 9 , 12

如果过面试为1,3,则可以获得1~7之间的每个邮资值,并且7就是可以得到的连续的邮资最大值

问题分析:

寻找子集就是子集树

寻找元素排列就是排列树

这是子集树问题,因为要求一个集合S,集合S中的元素满足某种性质。是从n个元素的集合S中,找出S满足某种性质的子集

思路:搜索可行解

解向量:用n元组x[1:n]表示n总不同邮票面值,从小到大排列

约束函数:若选定x[1:i-1],并且取值范围为:1~r,那么x[i]取值范围为x[i-1]+1~r+1

如何确定r的值?

计算x[1:1]的最大连续邮资区间时,直接递归复杂度较高。

尝试计算用不超过m张面值为x[1:i]贴出邮资k所需的最少邮票数为y[k],通过y[k]可以推算出r(最大连续邮资)的值,

y[k]可以通过递推在O(n)时间内解决

不懂

算法步骤:

1 初始化数组

2 计算任意邮资需要的最少张数

3 判断是否越界,并更新最优值

4 拷贝数组,对x[i]范围的x[i-1]+1 到 r,更新当前解,递归调用 , 回溯

输入:

2(邮票的种类数) 3(允许张贴的邮票数)

2 3

5 4

输出:

7

1 3

70

1 3 11 15 32

*/

#include <iostream>
#include <string.h>
using namespace std;
int n ,m ;     //邮票种类数,邮票允许的张帖数
int x[100];    //当前解
int bestx[100];//当前最优解
int y[10000];  //贴出各种邮资所需要的最少邮票数
int maxint;    //大整数
int maxl;      //邮资上界 
int maxvalue;  //当前最优值
 
void backTrace(int i ,int r)
{
	//递推求解任意邮资所需要的最少邮资数
	int z[10000];
	for(int j = 0 ; j <= x[i-2]*(m-1) ; j++)//这里是x-2
	{
		//如果小于最多邮资数
		if(y[j] < m)
		{
			for(int 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] < maxint )
	{
		r++;
	}
	//判断是否搜索到最优解
	if(i > n)
	{
		//判断是否超过最优解
		if(r - 1 > maxvalue)//?r - 1
		{
			maxvalue = r - 1;
			//更新最优解
			for(int p = 1 ; p <= n ; p++)
			{
				bestx[p] = x[p];
			}
			return ;//易错,直接退出
		}
	}
	//拷贝邮资最少张数数组,用于回溯,对于每个x[i]的可能值进行赋值并求解
	for(int k = 1 ; k <= maxl; k++)
	{
		z[k] = y[k];
	}
	for(int j = x[i-1] + 1 ; j <= r ; j++)
	{
		x[i] = j ;
		backTrace( i+1 , r);//递归下一个编号
		//回溯
		for(int k = 1 ; k <= maxl; k++)
		{
			y[k] = z[k];
		}
	}
}

void process()
{	
	while(cin >> n >> m)
	{
		maxint = 32767;
		maxl = 1500;
		maxvalue = 0;
		//初始化解
		memset(x , 0 , sizeof(x));
		//初始化邮资所需要的最少张数
		for(int i = 1 ; i <= maxl ; i++)
		{
			y[i] = maxint;
		}
		y[0] = 0;
		x[1] = 1;//第一个邮票面值必须为1
		backTrace(2,1);//第一个参数是邮票编号,第二个参数是最大连续邮资
		cout << maxvalue;
 
	}
}
 
int main(int argc,char* argv[])
{
	process();
	getchar();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/DorisBao1021/article/details/81090595
今日推荐