#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; }