码迷,mamicode.com
首页 > 其他好文 > 详细

UVa 817 According to Bartjens 题解

时间:2018-01-29 21:30:28      阅读:455      评论:0      收藏:0      [点我收藏+]

标签:.cpp   nbsp   ref   过程   http   输出   大致   div   比较   

难度:β

建议用时: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

UVa 817 According to Bartjens 题解

标签:.cpp   nbsp   ref   过程   http   输出   大致   div   比较   

原文地址:https://www.cnblogs.com/Alrond/p/8379259.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!