转载请注明出处:優 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
}

