#include<iostream> #include<cstring> using namespace std; //连续邮资问题 /* 某国发行了n种不同面值的邮票 并规定每封信最多允许贴m张邮票 在这些约束下,为了能贴出{1,2,3,... ,maxvalue}连续整数集合的所有邮资,并使maxvalue的值最大 应该如何设计各邮票的面值? 例如,当n=5、m=4时,面值设计为{1,3,11,15,32},可使maxvalue达到最大值70 或者说,用这些面值的1至4张邮票可以表示不超过70的所有邮资,但无法表示邮资71 而用其他面值的1至4张邮票如果可以表示不超过k的所有邮资,必有k<=70 */ //第一种邮票一定是面值为1的邮票,不然邮资1无法构造 //如果前x[1->i]种邮票在不超过m张的前提下能构造的邮资序列为1->r //则第x[i+1]种邮票面值的可选范围是x[i]+1->r+1 int n,m;//n种邮票贴m张 int maxstamp;//最大邮资 int r;//前x[1->cur]种邮票能支付的连续邮资的最大值 const int MM=500;//邮票能支付的最大邮资不超过MaxMoney const int MN=50;//邮票的种类不超过MaxN int x[MN]; //x[i]=k表示选用第i中邮票的面值为k,这是一个邮票面值依次递增的数组 int y[MM]; //y[j]=k表示前x[1->cur]种邮票支付邮资j时邮票的最少张数为k int ans[MN];//达到最大邮资的一组邮票面值 const int inf=10000;//y[j]=inf用来表示邮资j不可达 void backtrace(int cur){//寻找下标为cur的邮票面值 if(cur>=n){ if(r>maxstamp){ maxstamp=r; for(int i=0;i<n;i++){ ans[i]=x[i]; } } return; } int backup_y[MM]; for(int i=0;i<MM;i++){ backup_y[i]=y[i]; } int backup_r=r; for(int i=x[cur-1]+1;i<=r+1;i++){ x[cur]=i;//选取第cur张邮票的面值为i for(int k=0;k<=x[cur-1]*(m-1);k++){ if(y[k]>=m)continue;//k范围的含义是邮资从0到x[cur-1]*(m-1)的最少邮票数有可能小于m张 //邮资大于x[cur-1]*(m-1)的最少邮票数现阶段一定大于等于m张 for(int num=1;num<=m-y[k];num++){//用已有的最少邮票组合加上新选取的cur张邮票去更新y数组 if(k+x[cur]*num<MM){//邮资不能越界 y[k+x[cur]*num]=min(y[k+x[cur]*num],y[k]+num); //如果不超过m张的新的邮票组合构成的邮资对应的张数比新组合的张数大,则更新它 } } } while(y[r+1]<inf)r++; backtrace(cur+1); //回溯 for(int i=0;i<MM;i++){ y[i]=backup_y[i]; } r=backup_r; //回溯代表选取x[cur]=i结束,状态返回到cur-1时,并且接着for循环为cur选取新的i值 } } void print(){ cout<<maxstamp<<endl; for(int i=0;i<n;i++){ cout<<ans[i]<<" "; } cout<<endl; } int main(){ memset(y,inf,sizeof(y)); cin>>n>>m; x[0]=1;//x数组下标从0开始表示第1种邮票 y[0]=0;//支付邮资0需要0张邮票 r=m;//目前只有1种邮票面值为1,因此能支付的范围是1->m for(int i=1;i<=m;i++) y[i]=i; //一种邮票的情况下支付邮资i的最少邮票数为i backtrace(1);//寻找下标为1的邮票面值,即第二种邮票面值 print();//输出结果 return 0; }