题目链接
题目
| Time Limit: 1000MS | Memory Limit: 10000K | |
| Total Submissions: 6668 | Accepted: 3608 |
Description
1.The shredder takes as input a target number and a sheet of paper with a number written on it.
2.It shreds (or cuts) the sheet into pieces each of which has one or more digits on it.
3.The sum of the numbers written on each piece is the closest possible number to the target number, without going over it.
For example, suppose that the target number is 50, and the sheet of paper has the number 12346. The shredder would cut the sheet into four pieces, where one piece has 1, another has 2, the third has 34, and the fourth has 6. This is because their sum 43 (= 1 + 2 + 34 + 6) is closest to the target number 50 of all possible combinations without going over 50. For example, a combination where the pieces are 1, 23, 4, and 6 is not valid, because the sum of this combination 34 (= 1 + 23 + 4 + 6) is less than the above combination's 43. The combination of 12, 34, and 6 is not valid either, because the sum 52 (= 12 + 34 + 6) is greater than the target number of 50.
Figure 1. Shredding a sheet of paper having the number 12346 when the target number is 50
There are also three special rules :
1.If the target number is the same as the number on the sheet of paper, then the paper is not cut.
For example, if the target number is 100 and the number on the sheet of paper is also 100, then
the paper is not cut.
2.If it is not possible to make any combination whose sum is less than or equal to the target number, then error is printed on a display. For example, if the target number is 1 and the number on the sheet of paper is 123, it is not possible to make any valid combination, as the combination with the smallest possible sum is 1, 2, 3. The sum for this combination is 6, which is greater than the target number, and thus error is printed.
3.If there is more than one possible combination where the sum is closest to the target number without going over it, then rejected is printed on a display. For example, if the target number is 15, and the number on the sheet of paper is 111, then there are two possible combinations with the highest possible sum of 12: (a) 1 and 11 and (b) 11 and 1; thus rejected is printed. In order to develop such a shredder, you have decided to first make a simple program that would simulate the above characteristics and rules. Given two numbers, where the first is the target number and the second is the number on the sheet of paper to be shredded, you need to figure out how the shredder should "cut up" the second number.
Input
tl num1
t2 num2
...
tn numn
0 0
Each test case consists of the following two positive integers, which are separated by one space : (1) the first integer (ti above) is the target number, (2) the second integer (numi above) is the number that is on the paper to be shredded.
Neither integers may have a 0 as the first digit, e.g., 123 is allowed but 0123 is not. You may assume that both integers are at most 6 digits in length. A line consisting of two zeros signals the end of the input.
Output
sum part1 part2 ...
rejected
error
In the first type, partj and sum have the following meaning :
1.Each partj is a number on one piece of shredded paper. The order of partj corresponds to the order of the original digits on the sheet of paper.
2.sum is the sum of the numbers after being shredded, i.e., sum = part1 + part2 +...
Each number should be separated by one space.
The message error is printed if it is not possible to make any combination, and rejected if there is
more than one possible combination.
No extra characters including spaces are allowed at the beginning of each line, nor at the end of each line.
Sample Input
50 12346 376 144139 927438 927438 18 3312 9 3142 25 1299 111 33333 103 862150 6 1104 0 0
Sample Output
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
Source
题意
给出一串数字N,要求对N进行切割,求切割出的子数字的和最接近目标T但不大于目标T。在输出上,如果恰好N == T,那么就不切割,意思就是不对纸条进行操作(但是仍然正常输出),如果有多种输出结果,那么拒绝打印输出"rejected"。如果无法切割,也就是说无论怎么切割纸条,和总是大于T,那么输出"error"。如果能够切割就按照要求输出:切割后的和,以及每一个被切割的部分。
题解
第一次写的时候感觉自己还是不太懂得变通了。dfs的优点在于递归,这个题的大意就是通过搜索来枚举切割方式,同时配合剪枝将不可能得到正确答案的方式给剪掉。但是刚开始太过于想要使用dfs的递归了,认为找到一种方案,如果是当前最优值,就进行回溯,让对应的值储存到数组中。然后写起来就非常的麻烦。
搜题解的时候看到大佬的写法非常好,利用一个数组来储存答案,将dfs的搜索方式模拟成为一颗搜索树,然后再对树进行剪枝。那么就不需要利用回溯了,树的深度就是切割的数量,对每次dfs函数而言,就是对剩余的纸条枚举切割方式。不说废话直接上dfs的代码:
首先,这个题最基础的思想是按位搜索。因为是要剪裁纸条,那么对于完整的纸条N而言(假设有n位),那么我们可以如何剪裁?考虑dp的思想,将纸条分成2部分,第一部分是剪裁出的第一个子纸条的长度:1 - n,第二部分是剩余的纸条剪裁可能。那么直接将第二部分扔进dfs即可。所以,重点就是按位分解,利用取模+除,可以将一个数的每一位都分解出来并存进数组。这里就不多说了,主要是dfs的代码部分:
void dfs(int bit,int sum,int num){ // bit表示当前还剩多少位数,或者说剩余纸条长度。sum是当前这个树枝的累积和。num是树的当前深度。
if(sum > T) return; // 无法剪裁的情况,直接剪枝,一直进行回溯,到分叉点。
if(bit == 0){ // bit == 0 即剩余的纸条有0位,说明剪裁完成,树枝已经最长了,开始判定这个树枝是不是相对优的解,推荐先看下面的代码。
if(T - sum == MIN){ // 打印异常的情况,有多个最优解,直接剪枝。
flag = -1;
return;
}
if(T - sum > MIN) return; // 不是最优解,剪枝。
for(int i = 0;i < num;i++){ // 接下来就是对相对优解的操作,将剪裁的每一段纸的数据储存到相对优解中
ans[i] = son[i];
}
MIN = T - sum; // 更新最优值
ans_num = num; // 更新最优值的树的深度(剪裁的纸的份数)
flag = 1; // flag = 1表示存在一个解
return; // 结束这个搜索
}
int temp = 0;
for(;bit > 0;bit--){ // 搜索核心,给出剩余纸条的位数,并且模拟剪裁,即裁下bit位一直到裁下1位
temp = temp*10 + number[bit]; // 这一步是我没有想到的,这里利用temp可以模拟所有的剪裁方式,看不懂的自己在纸上模拟一下
son[num] = temp; // son数组就表示了当前这个树枝每一层的值,或者说是当前这个剪裁方法中每一段纸的值
dfs(bit-1,sum+temp,num+1); // 将剩余的纸进行下一次搜索,树枝变长
}
}
如果结合注释仍然看不懂,推荐在纸上模拟一下。我也不画搜索树了,大家自己脑补吧。
flag的值代表了这一棵搜索树的结果,如果有一个分支得到了-1的结果,也就是由多解的情况,那整棵搜索树结果一定是输出"rejected",就可以结束整棵树了。如果是0,那么说明没有一次找到解,就输出"error"。如果是2,说明存在最优解,让储存的数组输出即可。
C++ AC代码 0ms
#include<iostream>
#include<vector>
#include<string>
#include<math.h>
#include<algorithm>
#include<map>
#include<utility>
#include<queue>
#include<memory>
#include<stack>
#include<sstream>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MAX 310
#define INF 0x3f3f3f3f
using namespace std;
int T,N;
int MIN;
int flag;
int number[10],son[10],ans[10],ans_num;
void dfs(int bit,int sum,int num){
if(sum > T) return;
if(bit == 0){
if(T - sum == MIN){
flag = -1;
return;
}
if(T - sum > MIN) return;
for(int i = 0;i < num;i++){
ans[i] = son[i];
}
MIN = T - sum;
ans_num = num;
flag = 1;
return;
}
int temp = 0;
for(;bit > 0;bit--){
temp = temp*10 + number[bit];
son[num] = temp;
dfs(bit-1,sum+temp,num+1);
}
}
int main(){
TIME;
while(cin >> T >> N){
if(T == 0 && N == 0) break;
if(T == N){
cout << T << " " << T << endl;
continue;
}
int num = 0;
MIN = INF;
flag = 0;
while(N){
number[++num] = N % 10;
N /= 10;
}
dfs(num,0,0);
if(flag == 0){
cout << "error" << endl;
}else{
if(flag == -1){
cout << "rejected" << endl;
}else{
cout << T - MIN << " ";
for(int i = 0;i < ans_num;i++) cout << ans[i] << " ";
cout << endl;
}
}
}
system("pause");
return 0;
}
该博客详细介绍了POJ-1416题目,即根据给定数字N,寻找最接近目标T但不超过T的切割子数字和。博主分享了使用深度优先搜索(DFS)结合剪枝策略的解决方案,强调了按位搜索的重要性,并提供了C++的AC代码。文章探讨了如何避免复杂回溯,通过构建搜索树简化问题,以及如何处理多解和无法切割的情况。

719

被折叠的 条评论
为什么被折叠?



