class Stamp { friend int MaxStamp(int, int, int[]); private: void BackTrack(int i, int r); int n, //邮票面值数 m, //每张信封最多允许贴的邮票数 maxvalue, //当前最优值 maxint, //大整数 maxl, //邮资上界 *x, //当前解 *y, //贴出各种邮资所需最少邮票数 *bestx; //当前最优解 }; void Stamp::BackTrack(int i, int r) {//当前的邮票是x[i],r是x[1- i-1]邮票所能表示的最大的连续上界 int j; int k; for(int j=0; j<=x[i]*(m-1); j++) if(y[j]<m) for(int k=1; k<=m-y[j];k++) if(y[j]+k<y[j+x[i]*k]) y[j+x[i]*k]=y[j]+k; while(y[r]<maxint) r++;//求得x[1-i]的邮票所能表示的最大连续上界 --r; if(i>=n) { if(r>maxvalue) { maxvalue=r; for(int j=1;j<=n;j++) bestx[j]=x[j]; } return; } int *z=new int [maxl+1]; for(int k=1;k<=maxl;k++) z[k]=y[k]; for(j=x[i]+1;j<=r+1;j++)//下一个区间的取值范围从[r+1,...],所以必须保证有值能取到r+1,因为这个区间能取到的最大连续值是r { if (y[r+1 - j] < m) { x[i+1]=j; BackTrack(i+1,r);//r代表当前区间能取到的最大连续值 for(k=1;k<=maxl;k++) y[k]=z[k]; } } delete [] z; } int MaxStamp(int n, int m, int bestx[]) { int i; Stamp X; int maxint=32767; int maxl=1500; X.n=n; X.m=m; X.maxvalue=0; X.maxint=maxint; X.maxl=maxl; X.bestx=bestx; X.x=new int [n+1]; X.y=new int [maxl+1]; for(int i=0;i<=n;i++) X.x[i]=0; for(i=1;i<=maxl;i++) X.y[i]=maxint; X.x[1]=1; X.y[0]=0; X.BackTrack(1,0);//这样取值比王晓东书上的更好理解了,0前一个区间能取到的最大连续值是0,这是因为前一个区间没有任何邮票的面值 delete [] X.x; delete [] X.y; return X.maxvalue; } int main() { int n,m; // cout<<"请输入邮票面值数(n)和每张信封最多允许贴出的邮票数(m):"; while(scanf("%d%d",&n,&m)) { int *bestx=new int [n+1]; int maxvalue; maxvalue=MaxStamp(n,m,bestx); cout<<"最优解为: "; for(int i=1;i<=n;i++) cout<<bestx[i]<<" "; cout<<"\n"<<"最大连续邮资区间为:"<<maxvalue<<endl; } return 0; }