转载请注明出处:優 YoU http://user.qzone.qq.com/289065406/blog/1304031265
题目翻译
:
公司现在要发明一种新的碎纸机,要求新的碎纸机能够把纸条上的数字切成最接近而不超过 target 值。比如, target 的值是 50 ,而纸条上的数字是 12346 ,应该把数字切成四部分,分别是 1 、 2 、 34 、 6 。因为这样所得到的和 43 (= 1 + 2 + 34 + 6) 是所有可能中最接近而不超过 50 的。(比如 1, 23, 4, 和 6 就不可以 , 因为它们的和不如 43 接近 50 ,而 12, 34, 6 也不可以,因为它们的和超过 50 了。碎纸还有以下三个要求:
1
、如果
target
的值等于纸条上的值,则不能切。
2
、如果没有办法把纸条上的数字切成小于
target
,则输出
error
。如
target
是
1
而纸条上的数字是
123
,则无论你如何切得到的和都比
1
大。
3
、如果有超过一种以上的切法得到最佳值,则输出
rejected
。如
target
为
15
,纸条上的数字是
111
,则有以下两种切法
11
、
1
或者
1
、
11.
你的任务是编写程序对数字进行划分以达到最佳值。
解题思路:
用
DFS
深搜
(1)
比如一个
6
位数
n
,切成为
6
个数的话,这
6
个数的和如果大于目标数
aim
则不用再搜索了,因为这肯定是所有划分中和最小的,而最小都比目标数大,自然就没有合要求的答案了
.
(2)
如何切分,假如以
50
12346
为例。
第一步,先切下一个
“
第二步,再切下一个
“
第三步,再切下一个
“
第四步,再切下一个
“
第五步,再切下
“
(3) 切下来的 前面的数字串部分 则加入到划分的和,剩下的部分继续递归,直到剩下的数字串长度为 0 。 可以用一个 int 记录划分方式 (int p) , 如上例的输入为 50 12346 时,其结果为 43 1 2 34 6 ,那么 p=1121 ,代表把 12346 划分为 4 部分,第一部分为第 1 位,第二部分为第 2 位,第三部分为第 3 、 4 位,第四部分为第 5 位
(4) 注意在搜索时,必须把 n 的 剩余数字部分 转化为字符串再搜索,不然若 剩余的数字开头第一位为 0 时,会导致出错。
(5) 剪枝方法:在搜索时若发现部分和 大于(不能等于) aim 时,则可结束搜索。
(6)error 的判定要在搜索前进行, rejected (多个最优解)的判定要在搜索后判定。
(7) 关于出现相同最优解的标记,每出每种划分的 sum 每出现一次标记 +1 ,要使标记为 O(1) ,只需把 vist 数组开得足够大。 N 最多为 6 位数,因此 Maxsum=999999
简单的附上一个关于例 50 12346 的不完全搜索树
省略号为未列出的结点
1 // Memory Time
2 // 4160K 157MS
3
4 #include < iostream >
5 #include < cmath >
6 #include < string >
7 using namespace std;
8
9 int getlen( int n) // 得到n的位长度
10 {
11 if (n < 10 )
12 return 1 ;
13 else if (n < 100 )
14 return 2 ;
15 else if (n < 1000 )
16 return 3 ;
17 else if (n < 10000 )
18 return 4 ;
19 else if (n < 100000 )
20 return 5 ;
21 else
22 return 6 ;
23 }
24
25 int getvalue( char * s, int i) // 得到数字字符串s前i位字符(数字)组成的int值
26 {
27 int k = i;
28 int sum = 0 ;
29 while (k)
30 {
31 k -- ;
32 sum += (s[k] - ' 0 ' ) * ( int )pow( 10.0 ,( double )(i - k - 1 ));
33 }
34 return sum;
35 }
36
37 int gethead( int n, int i) // 得到由n的前i位数字构成的int
38 {
39 int len = getlen(n);
40 if (len <= i)
41 return n;
42 return n / ( int )pow( 10.0 ,( double )(len - i));
43 }
44
45 int gettail( int n, int i) // 得到由n的后i位数字构成的int
46 {
47 return n % ( int )pow( 10.0 ,( double )i);
48 }
49
50 int aim; // 目标数
51
52 int result; // 最优划分的和
53 int path; // 最优划分的划分方式
54
55 int sum; // 某种划分的和
56 int p; // 某种划分方式
57
58 int vist[ 1000000 ]; // 记录每个sum出现的次数
59 // 999999是当n=999999时的最大和值
60
61 void DFS( char * s, int len)
62 {
63 if (len == 0 )
64 {
65 vist[sum] ++ ;
66 if (sum > result && sum <= aim)
67 {
68 result = sum;
69 path = p;
70 }
71 return ;
72 }
73
74 for ( int i = 1 ;i <= len;i ++ )
75 {
76 int a = getvalue(s,i); // n的前i位字符转变为数字留下,计入部分和
77 sum += a; // 部分和
78 if (sum > aim) // 剪枝,部分和已经大于aim,无需继续往下搜索
79 {
80 sum -= a;
81 continue ;
82 }
83 p = p * 10 + i; // 记录划分方式
84
85 char b[ 7 ]; // 构造n的后i位字符序列,继续递归
86 int j = 0 ;
87 for ( int k = i;k < len;k ++ )
88 b[j ++ ] = s[k];
89 b[j] = ' \0 ' ;
90
91 DFS(b,len - i);
92
93 sum -= a; // 回溯
94 p /= 10 ;
95 }
96 return ;
97 }
98
99 int main( void )
100 {
101 while ( true )
102 {
103 /* Input */
104
105 char s[ 7 ]; // 打印纸上的数字
106 cin >> aim >> s;
107
108 int len = strlen(s);
109 int n = getvalue(s,len); // 构造s的数字序列n
110
111 if ( ! aim && ! n)
112 break ;
113
114 if (aim == n) // 目标值与打印纸上的数字一致
115 {
116 cout << aim << ' ' << n << endl;
117 continue ;
118 }
119
120 int num = n; // temporary
121 int k = 0 ; // n的各位数字之和
122 while (num)
123 {
124 k += num % 10 ; // 逐位划分是 和最小的划分方式
125 num /= 10 ;
126 }
127 if (k > aim) // 最小和也大于aim,则所有划分都大于aim
128 {
129 cout << " error " << endl;
130 continue ;
131 }
132
133 /* Initial */
134
135 result =- 1 ;
136 sum = 0 ;
137 path = 0 ;
138 p = 0 ;
139 memset(vist, 0 , sizeof (vist));
140
141 /* DFS */
142
143 DFS(s,len);
144
145 /* Output */
146
147 if (vist[result] > 1 ) // 最优解多于一个
148 cout << " rejected " << endl;
149 else if (vist[result] == 1 ) // 有唯一最优解
150 {
151 cout << result << ' ' ;
152
153 int L = getlen(path); // 输出划分的方式
154 for ( int i = 1 ;i <= L;i ++ )
155 {
156 int k = gethead(path, 1 ); // 取path的第一位k,k的值等于n的第一段划分位数,即从n的第1位到第k位
157 cout << gethead(n,k) << ' ' ;
158 n = gettail(n,len -= k);
159 path = gettail(path,L - i);
160 }
161 cout << endl;
162 }
163 }
164 return 0 ;
165 }