难度:β
建议用时:40 min
实际用时:4 h
题目:??
代码:??
这题有两个坑点首先要注意:
1)对于 “2000=” 要特判。应该判为 “IMPOSSIBLE”
2)枚举顺序为 “*+-” 然后是数字。
好了。这题的 DFS 过程很简单了。主要的是怎样计算一个字符串多项式(不带除号的)。
代码链接:??(这里我把除号带上了,原理跟减号一样的)
从最简单的加法开始。
1+2+3
怎样计算上面的式子?
我选择用递归。
从左到右(不是遍历,这里因该有一个区间)查看当前区间里的每一个元素,如果是 “+”,就把左右分开,分别调用后相加返回。
大致是这样的:
1 double calculate(int l, int r, string n_str) { 2 for (int i = l; i < r; i++) 3 if (n_str[i] == ‘+‘) 4 return calculate(l, i, n_str) + calculate(i+1, r, n_str); 5 }
这是伪码,没有真实作用。(真?代码在上面的链接里)
注意,这里我们实际上是在带括号计算。举个例子:
1+2+3+4
如果这时从 1 和 2 之间的加号分开,那么实际上下面的过程是分别算两边的代数值。
(1)+(2+3+4)
不难发现,这样一来加法和乘法都好说,但是如果遇到间伐或除法就麻烦了。
1-2+3+4 = (1)-(2+3+4)
这显然是不对的。怎么办呢?干脆模拟一下,变个号。
1-2+3+4 = (1)-(2-3-4)
具体到代码就是:
1 if (n_str[i] == ‘-‘) { 2 int j = i+1; 3 while (j != n_str.size()) { // 下面把后面的符号反过来。 4 if (n_str[j] == ‘-‘) n_str[j] = ‘+‘; 5 else if (n_str[j] == ‘+‘) n_str[j] = ‘-‘; // 注意 else,否则就白费功夫了。 6 j++; 7 } 8 return calculate(l, i, n_str) - calculate(i+1, r, n_str); 9 }
乘法和除法的处理是类似的。但是既然乘除法的优先级比加减法高,那么就应该先从加减法剖开,后从乘除法剖开。乘除法要在最后计算。
另外,如果发现式子里没有任何符号了,说明这时处理的是数字。直接计算返回就好了。
注意:小心在做除法时溢出。最好用浮点数记录中间值。
上面分析了计算,下面就来看主算法。
这里的主算法很简单,不啰嗦,上代码:
1 bool dfs(int cur_d, int cur_idx, bool ok) { 2 if (cur_idx == str.size()) { 3 if (try_cal(cur_d)) { 4 output(cur_d); 5 return true; 6 } 7 return false; 8 } 9 bool update = false; 10 for (int i = 0; i < 4; i++) { 11 if (i != 3 && ok) { 12 temp[cur_d] = oprt[i]; 13 if (dfs(cur_d+1, cur_idx, false)) update = true; 14 } 15 if (i == 3) { 16 temp[cur_d] = str[cur_idx]; 17 if (dfs(cur_d+1, cur_idx+1, true)) update = true; 18 } 19 } 20 return update; 21 }
我在调用时加入一个 Bool 值,表示可不可以在这一层加上符号。显然如果上一层是符号,这一层就不能用符号。开始调用时设为 false,因为第一个位置也不能用符号。
这样可以保证枚举时不会出现连续两个符号的情况。
一旦枚举出一种情况,立刻进行判断是否合法,然后输出。这没什么好说的。
判断有两种:一要确保代数和为 2000, 二要符合代数式的规矩,就是数字除 0 本身外不能以 0 开头(012),0 不能在开头连续多次出现(000),
最后一位不能是符号(12*),第一位也不能是符号(+123)(虽然这实际上是合理的,但是题目规定不能这样)。
1 bool qualify(string n_str) { 2 for (int i = 0; i < n_str.size(); i++) { 3 if (i == (int)n_str.size()-1 && is_operator(n_str[i])) return false; 4 if (!i && n_str[i] == ‘0‘ && !is_operator(n_str[i+1])) return false; 5 if (!i && is_operator(n_str[i])) return false; 6 if (is_operator(n_str[i-1]) && n_str[i] == ‘0‘ && !is_operator(n_str[i+1]) && i != (int)n_str.size()-1) return false; 7 } 8 return true; 9 }
这一段的调试很重要。上面计算的调试也很重要。大多数时间都应该花在调试这两个函数上面。
这样以来,这题就搞定了。思路并不难,主要是调试比较多。因为我是第一次写多项式的计算函数,所以在这上面花的时间比较多。
2018-01-29