http://poj.org/problem?id=1416
题意 : 要为碎纸机公司开发一种新的碎纸机,这种碎纸机要具有3个特性 :一是粉碎机以一个目标数 t 作为输入,并且粉碎的纸上写有一个数字n,二是碎纸机将纸粉碎成碎片的时候每张碎片上都要有这个数字的一位或者数位,三是每张碎片上数字的总和尽可能地接近目标数 t ,但不可以超过 t ,可以等于 t 。如果目标数与纸张上的数字相同,则不要粉碎,如果任何组合的总和不可能小于或者等于目标数 t ,就输出error,例如,如果目标数 t = 1,但是纸上的数是123的话,就算最小的组合也是6,所以不满足要求。还有若是有多个符合要求的,也就是说有多个总和接近目标数 t 但是没有超过目标数 t的,就输出rejected。最后输出的话,除了error还有rejected,另一种输出就是先输出符合条件的组合加起来的总和,再输出这个组合的每一部分,空格隔开
思路 : 所有碎纸片上的最佳数字和为max,所以要先判断和为max的拆分方案数是1还是大于1还是等于0,,而求这个最佳数字和max,我们从右往左按位拆分,若当前拆分出 i 位数,则n%(10^i),为当前碎纸片上的数字,剩余数字n/(10^ i )待拆分,而拆分有两种情况 :
不断开 : 当前碎纸片上的数字继续向左扩展
断开 : 拆分出当前碎纸片上的数字,所以,可以采用回溯法分头递归这两种情况
#include<cstdio> #include<cstring> #include<iostream> const int maxn = 1001 ; int m,n; int ans[maxn],t[maxn]; int maxx,r; int ansk; void dfs(int n,int sum,int now,int k,int p) {//n为待拆分数字,sum为已拆分出的输的和,now为当前待拆分出的一个数字,准备填入第k张碎纸片, //p为下一个十进制位的权 if(n==0) { t[k]=now; if(sum+now>m) return;//若拆分出的数和大于目标数则退出, if(sum+now==maxx) r++;//若拆分出的数和相同于maxx,那就使组合个数加1 else if(sum+now>maxx)//若拆分出的数和更佳,就更新为maxx { maxx=sum+now; r=1;//数和为maxx的组合为1 ansk=k;//已填数的碎纸片数和各张碎纸片数的填数记入最佳方案 for(int i=1; i<=k; i++) ans[i]=t[i]; } return ;//回溯 } int mm=n%10;//取待拆分数的个位数 dfs(n/10,sum,now+p*mm,k,p*10);//递归不断开的情况 t[k]=now; dfs(n/10,sum+now,mm,k+1,10);//断开的情况 } int main() { while(scanf("%d%d",&m,&n)&&m&&n) { maxx=0,r=0; dfs(n/10,0,n%10,1,10); if(maxx==0) { printf("error\n"); continue; } if(r>1) { printf("rejected\n"); continue; } printf("%d",maxx); for(int i=ansk; i>=1; i--) printf(" %d",ans[i]); printf("\n"); } return 0; }