算法设计与分析:第五章 回溯法 5.9连续邮资问题

来源:互联网 发布:js对象数组重新赋值 编辑:程序博客网 时间:2024/06/08 16:26
/*连续邮资问题:假设国家发行了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 35 4输出:71 3701 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;//第一个邮票面值必须为1backTrace(2,1);//第一个参数是邮票编号,第二个参数是最大连续邮资cout << maxvalue;}}int main(int argc,char* argv[]){process();getchar();return 0;}

0 0