2803:碎纸机
http:、、bailian.openjudge.cn/practice/2803/
- 总时间限制:
- 1000ms 内存限制:
- 65536kB
- 描述
- 你现在负责设计一种新式的碎纸机。一般的碎纸机会把纸切成小片,变得难以阅读。而你设计的新式的碎纸机有以下的特点:
1.每次切割之前,先要给定碎纸机一个目标数,而且在每张被送入碎纸机的纸片上也需要包含一个数。
2.碎纸机切出的每个纸片上都包括一个数。
3.要求切出的每个纸片上的数的和要不大于目标数而且与目标数最接近。
举一个例子,如下图,假设目标数是50,输入纸片上的数是12346。碎纸机会把纸片切成4块,分别包含1,2,34和6。这样这些数的和是43 (= 1 + 2 + 34 + 6),这是所有的分割方式中,不超过50,而又最接近50的分割方式。又比如,分割成1,23,4和6是不正确的,因为这样的总和是34 (= 1 + 23 + 4 + 6),比刚才得到的结果43小。分割成12,34和6也是不正确的,因为这时的总和是52 (= 12 + 34 + 6),超过了50。
还有三个特别的规则:
1.如果目标数和输入纸片上的数相同,那么纸片不进行切割。
2.如果不论怎样切割,分割得到的纸片上数的和都大于目标数,那么打印机显示错误信息。
3.如果有多种不同的切割方式可以得到相同的最优结果。那么打印机显示拒绝服务信息。比如,如果目标数是15,输入纸片上的数是111,那么有两种不同的方式可以得到最优解,分别是切割成1和11或者切割成11和1,在这种情况下,打印机会显示拒绝服务信息。
为了设计这样的一个碎纸机,你需要先写一个简单的程序模拟这个打印机的工作。给定两个数,第一个是目标数,第二个是输入纸片上的数,你需要给出碎纸机对纸片的分割方式。 输入 - 输入包括多组数据,每一组包括一行。每行上包括两个正整数,分别表示目标数和输入纸片上的数。已知输入保证:两个数都不会以0开头,而且两个数至多都只包含6个数字。
输入的最后一行包括两个0,这行表示输入的结束。
输出 - 对每一组输入数据,输出相应的输出。有三种不同的输出结果:
sum part1 part2 ...
rejected
error
第一种结果表示:
1.每一个partj是切割得到的纸片上的一个数。partj的顺序和输入纸片上原始数中数字出现的次序一致。
2.sum是切割得到的纸片上的数的和,也就是说:sum = part1 + part2 +...
第一种结果中相邻的两个数之间用一个空格隔开。
如果不论怎样切割,分割得到的纸片上数的和都大于目标数,那么打印“error”。
如果有多种不同的切割方式可以得到相同的最优结果,那么打印“rejected”。
样例输入 -
50 12346 376 144139 927438 927438 18 3312 9 3142 25 1299 111 33333 103 862150 6 1104 0 0
样例输出 -
43 1 2 34 6 283 144 139 927438 927438 18 3 3 12 error 21 1 2 9 9 rejected 103 86 2 15 0 rejected
解题思路:因为最多只有6位数,所以可以一个一个的测试,暴力法!!最开始先判断一下能不能得到结果,输入的数有多少位,就分解为多少个一位数,这样加起来的结果一定是所有结果中最小的,如果这个最小的结果都大于了你的目标的结果,那么就只有输出“error”,否则的话就可以开始递归寻找最接近目标的结果。用一个bool型的变量来表示是不是有相同的最优的结果,如果最优的结果有两个或者两个以上,就会将flag置为true,否则的话flag就会始终为false。因为最后还要输出分好的每一个数,所以定义temp数组来表示每一层的结果,如果这一次得到的最终的结果比上一次的要好,那么就将temp数组复制到part数组里面,这样就可以最后输出。我开始的时候没有设这个temp数组,只在每次满足条件,调用下一层函数的时候,就将part的当前的数更新,这样的话,最后得到的part数组始终是最后一次递归的,这样的话就错了,所以我还是定义了temp数组来保存该次的每一部分的数是多少。递归函数的主体的算法思路是这样的:从当前的下标开始,将该从下标以后的字符串开始分割,先剪下来一个,将剪下来的结果存入temp[递归的层数]中,然后判断temp[……]+result 是不是小于等于目标结果res的,如果满足条件的话就调用下一层函数,将剩下的字符串的开始的下标(p+i)作为参数传入,然后再减下来两个(如果满足条件的话),然后以剩下的字符串的开始的下标(p+i)作为第二个参数传如调用的函数里面………………,最后,当字符串被遍历到最后的时候,判断当前的result是否大于了目前已经存在的max(前面得到的结果,如果是第一次的话,这个max已经在主函数里面初始化为0了),但是在判断result是不是大于max之前,要先判断max是不是等于result的,如果是等于的话就得将flag置为true(因为如果在将max的值改变之后再判断的话,那么接下来的max就始终是等于result的,那么就会导致flag始终为true。 )。如果max小于了result,就将max的值更新,然后再将当前存入的temp数组复制到part数组里面。递归函数大概就是这样的。当然还有其他的思路和写法比我这更好#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int mymax = 0; char a[7]; bool flag; int res; int part[6]; int temp[6]; int number; void fun(int result, int p,int len,int num)//当前的结果,当前数组开始的下标,数组长度 {int i;if(p == len){if(mymax == result){flag = true;}if(mymax < result){number = num;mymax = result;flag = false;for(i = 0;i<number;i++){part[i] = temp[i];}}return;}int j,n = 1;for(i=1;i<=len-p;i++)//取长度为i的字符串{temp[num] = 0;for(j=p,n = 0;n < i;n++,j++)//计算值{temp[num] = temp[num]*10 + a[j] - '0';}if(temp[num] + result <= res){//part[num] = temp;//这句话不能放这儿fun(result + temp[num],p+i,len,num+1);}} } int main() {int len;int i;int sum;while(scanf("%d%s",&res,a)){mymax = 0;sum = 0;if(res == 0 && strcmp(a,"0") == 0)break;len = strlen(a);for(i = 0;i<len;i++){sum = sum + a[i] - '0';}if(sum > res)//因为都分为个位数,然后加起来是最小的,所以如果这个最小的数都大于了需要的数的话,那么就是error{printf("error\n");}else//否则的话就是可以得到,所以就开始判断{fun(0,0,len,0);if(flag == false){printf("%d",mymax);for(i=0;i<number;i++){printf(" %d",part[i]);}printf("\n");}elseprintf("rejected\n");}//printf("%d %s\n",res,a);}return 0; }