题目
知识点
- 回溯法
思路
- 一开始没有很好的思路解决"括号"的问题, 每次选择一个数字进行“加减乘除”计算的话,括号没法做
- 其实本身来说总共就4个数字,所有符号、括号情况全部枚举出来时间上也没有太大的压力。
- 思路可以是这样:
-
游戏的第一步是挑出两个数,算出一个新数替代这两个数,放入数组中
那么此时:[(数字1-数字2),数字3,数字4]
(- 代表任意一个运算符) -
然后,在三个数中玩 24 点,再挑出两个数,算出一个数替代它们。
如果选择了上面的第一个元素(即(数字1-数字2))和数字3,那么此时就是:
[((数字1-数字2)- 数字3),数字4]
这样的话就实现了括号的枚举,如果没有选择第一个元素,选择了后面的两个元素,即:
[(数字1-数字2),(数字3-数字4)]
-
然后,在两个数中玩 24 点……
-
- 通过这样的方式,我们就可以把所有的运算符以及括号的所有可能枚举出来了,找到第一个返回
true
即可。 - 注意可能存在分数的情况,造成精度的损失,因此不直接使用
rest[0]==21
,而是if (fabs(rest[0] - 21) <= 0.0000001)
判断是否满足条件(Line 5)。
代码
有一组数据[1,7,4,5]
怎么过都过不了很奇怪(我自己也没看出来怎么组成21,答案是true)!
class Solution {
public:
bool dfs(vector<int> &cards, int depth, vector<double> rest) {
if (depth == 3) {
if (fabs(rest[0] - 21) <= 0.0000001) return true;
return false;
}
int len_rest = rest.size();
for (int i = 0; i < len_rest; i++) {
for (int j = i; j < len_rest; j++) {
if (i == j) continue;
vector<double> temp = rest;
temp.erase(find(temp.begin(), temp.end(), rest[i]));
temp.erase(find(temp.begin(), temp.end(), rest[j]));
// 加法
vector<double> temp1 = temp;
double add = rest[i] + rest[j];
temp1.emplace_back(add);
if (dfs(cards, depth + 1, temp1)) return true;
// 减法
temp1 = temp;
double subtract = rest[i] - rest[j];
temp1.emplace_back(subtract);
if (dfs(cards, depth + 1, temp1)) return true;
temp1.pop_back();
temp.emplace_back(-subtract); // 相反的情况
if (dfs(cards, depth + 1, temp1)) return true;
// 除法
temp1 = temp;
double devide = rest[i] / rest[j];
temp1.emplace_back(devide);
if (dfs(cards, depth + 1, temp1)) return true;
temp1.pop_back();
temp1.emplace_back(rest[j] / rest[i]); // 相反的情况
if (dfs(cards, depth + 1, temp1)) return true;
// 乘法
temp1 = rest;
double mutiply = rest[i] * rest[j];
temp1.emplace_back(mutiply);
if (dfs(cards, depth + 1, temp1)) return true;
}
}
return false;
}
bool judgePoint24(vector<int> &cards) {
return dfs(cards, 0, vector<double>(cards.begin(), cards.end()));;
}
};