动态规划实例之 Alphacode
1.问题描述:
Alphacode 题目描述
2.问题背景:
动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。
3.问题分析:
在开始做这道题的时候我首先想到的是用分治的思想来求解这道题(使用递归来做),但后来才发现这样会导致重复记数,并且很难解决这个问题;然后才想起能使用动态规划的思想来解决这个问题,好的废话少说,进入正题。
结合题目背景,我们从无到有逐个增加数字字符,首先设置两个状态标识pre_status(加入当前的数字字符str[i]前,有多少种译码方法), s_pre_status(加入数字字符str[i-1]前,有多少种译码方法);另外使用total_num来记录最终的译码方案。然后,确定状态转移方程,如果当前的数字字符str[i]能和前一个字符str[i - 1]组合成一个合法的译码数值(10 < int_value < 27),那么total_num = pre_status + s_pre_status;否则,total_num = pre_status;然后这两种情况都要s_pre_status = pre_status,
pre_status = total_num完成状态的转移。
4.问题注意事项:
1.确定字符0是否为当前字符,如果不是就正常按照之前的状态转移算法处理;如果是total_num = s_pre_status,因为0必须和当前字符的前一个字符组合成一个合法的整数值(比如20,10等,0不能单独进行进行译码,由题意可知)。
2.在算法的初始阶段,注意正确确定每个状态的合法值。(具体请参见代码)
5.问题参考解答:
#include<iostream>
07.
#include<string>
08.
using
namespace
std;
09.
/*判断能否与前一个字符组成合法值*/
10.
bool
isValid(string str,
int
i) {
11.
if
(i == 0)
return
false
;
12.
int
num = (str[i - 1] -
'0'
) * 10 + str[i] -
'0'
;
13.
return
(num > 10 && num <= 26);
14.
}
15.
16.
int
main() {
17.
string str;
18.
while
(cin >> str && str != string(
"0"
)) {
19.
int
total_num = 0;
20.
/*record the previous status*/
21.
int
pre_status = 0, s_pre_status = 0;
22.
23.
for
(
int
i = 0; i < str.length(); i++) {
24.
if
(i == 0) {
25.
total_num = s_pre_status = 1;/*第一个字符,total_num和s_pre_status置为1*/
26.
}
27.
if
(i == 1) {
28.
if
(isValid(str, i) && str[i] !=
'0'
) {/*注意这里有两个判断条件,特别是后者*/
29.
total_num = pre_status = 2;
30.
}
else
{
31.
total_num = pre_status = 1;/*如果第二个字符为0,那么必须和第一个字符组合*/
32.
}
33.
}
34.
35.
if
(i > 1) {
36.
if
(str[i] !=
'0'
) {
37.
if
(isValid(str, i)) {/*如果当前字符能和上一个字符组合,则总的种类数为前两个种类数之和(此时可以与上个字符结合或不结合)*/
38.
total_num = pre_status + s_pre_status;
39.
s_pre_status = pre_status;
40.
pre_status = total_num;
41.
}
else
{
42.
total_num = pre_status;/*如不能组合,则总数为上一个种类数*/
43.
s_pre_status = pre_status;
44.
pre_status = total_num;
45.
}
46.
}
else
{
47.
total_num = s_pre_status;/*如果当前字符为0,total_num就为上上步的种类数,因为上个字符要与当前字符结合*/
48.
s_pre_status = pre_status;
49.
pre_status = total_num;
50.
}
51.
}
52.
}
53.
cout << total_num << endl;
54.
}
55.
}