连续邮资问题
【问题描述】
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;
}