一【题目类别】
- 回溯
二【题目难度】
- 困难
三【题目编号】
- 679.24 点游戏
四【题目描述】
- 给定一个长度为4的整数数组
cards
。你有4
张卡片,每张卡片上都包含一个范围在[1,9]
的数字。您应该使用运算符['+', '-', '*', '/']
和括号'('
和')'
将这些卡片上的数字排列成数学表达式,以获得值24
。 - 你须遵守以下规则:
- 除法运算符
'/'
表示实数除法,而不是整数除法。- 例如,
4 /(1 - 2 / 3)= 4 /(1 / 3)= 12
。
- 例如,
- 每个运算都在两个数字之间。特别是,不能使用
“-”
作为一元运算符。- 例如,如果
cards =[1,1,1,1]
,则表达式“-1 -1 -1 -1”
是 不允许 的。
- 例如,如果
- 你不能把数字串在一起
- 例如,如果
cards =[1,2,1,2]
,则表达式“12 + 12”
无效。
- 例如,如果
- 除法运算符
- 如果可以得到这样的表达式,其计算结果为
24
,则返回true
,否则返回false
。
五【题目示例】
-
示例 1:
- 输入: cards = [4, 1, 8, 7]
- 输出: true
- 解释: (8-4) * (7-1) = 24
-
示例 2:
- 输入: cards = [1, 2, 1, 2]
- 输出: false
六【题目提示】
cards.length == 4
1 <= cards[i] <= 9
七【解题思路】
- 首先要读懂题意,搞清楚四种运算方式
- 然后使用回溯解决该问题
- 每次取出两个数字,进行相应的运算
- 然后组成新的数组,该数组包括运算后的结果和剩下的数字
- 然后递归重复进行运算
- 如果最后发现满足24点的要求即可返回真,否则返回假
- 有些细节需要注意:
- 注意精度问题,因为是实数运算,所以需要使用浮点类型
- 注意除法中的除数不能为零
- 加法和乘法满足交换律,可以通过剪纸减少计算次数,从而提高效率
- 最后返回结果即可
- 具体细节可以参考下面的代码
八【时间频度】
- 时间复杂度: O ( 1 ) O(1) O(1)
- 空间复杂度: O ( 1 ) O(1) O(1)
九【代码实现】
- Java语言版
class Solution {
// 目标值
private static final double target = 24;
// 误差
private static final double deviation = 1e-6;
// 四种运算,注意加和乘放在前两位,因为这两种运算满足交换律,方便后续处理
private static final int add = 0;
private static final int mul = 1;
private static final int sub = 2;
private static final int div = 3;
public boolean judgePoint24(int[] cards) {
// 将int数组转成double类型的List
List<Double> cardList = new ArrayList<>();
for (int card : cards) {
cardList.add((double)card);
}
// 返回最终结果
return dfs(cardList);
}
// 使用回溯判断满足运算结果为24的情况
private boolean dfs(List<Double> cards) {
// 边界条件
if (cards.size() == 0) {
return false;
}
// 返回结果
if (cards.size() == 1) {
return Math.abs(cards.get(0) - target) < deviation;
}
// 任取数组中的两个数
for (int i = 0; i < cards.size(); i++) {
for (int j = 0; j < cards.size(); j++) {
// 取得数不能相同
if (i != j) {
// 将剩下的数存到新的数组中
List<Double> newCards = new ArrayList<>();
for (int k = 0; k < cards.size(); k++) {
if (k != i && k != j) {
newCards.add(cards.get(k));
}
}
// 开始根据四种运算对最外层循环取出的两个数进行运算并将运算结果放到新的数组中
for (int l = 0; l < 4; l++) {
// 剪枝,满足交换律的运算(比如加和乘)只运算一次,提高效率
if (l < 2 && i > j) {
continue;
}
// 加、乘、减、除四种运算
double x = cards.get(i);
double y = cards.get(j);
if (l == add) {
newCards.add(x + y);
} else if (l == mul) {
newCards.add(x * y);
} else if (l == sub) {
newCards.add(x - y);
} else if (l == div) {
if (y < deviation) {
continue;
}
newCards.add(x / y);
}
// 根据当前运算结果进行下一次运算
if (dfs(newCards)) {
return true;
}
// 回溯,开始使用下一种运算符进行运算
newCards.remove(newCards.size() - 1);
}
}
}
}
// 所有情况都没满足
return false;
}
}
- Python语言版
class Solution:
def judgePoint24(self, cards: List[int]) -> bool:
# 目标值
target = 24
# 误差
deviation = 1e-6
# 四种运算,注意加和乘放在前两位,因为这两种运算满足交换律,方便后续处理
add = 0
mul = 1
sub = 2
div = 3
# 使用回溯判断满足运算结果为24的情况
def dfs(cards):
# 边界条件
if not cards:
return False
# 返回结果
if len(cards) == 1:
return abs(cards[0] - target) < deviation
# 任取数组中的两个数
for i, x in enumerate(cards):
for j, y in enumerate(cards):
# 取得数不能相同
if i != j:
# 将剩下的数存到新的数组中
new_cards = list()
for k, z in enumerate(cards):
if k != i and k != j:
new_cards.append(z)
# 开始根据四种运算对最外层循环取出的两个数进行运算并将运算结果放到新的数组中
for l in range(4):
# 剪枝,满足交换律的运算(比如加和乘)只运算一次,提高效率
if l < 2 and i > j:
continue
# 加、乘、减、除四种运算
if l == add:
new_cards.append(x + y)
elif l == mul:
new_cards.append(x * y)
elif l == sub:
new_cards.append(x - y)
elif l == div:
if abs(y) < deviation:
continue
new_cards.append(x / y)
# 根据当前运算结果进行下一次运算
if dfs(new_cards):
return True
# 回溯,开始使用下一种运算符进行运算
new_cards.pop()
# 所有情况都没满足
return False
# 返回最终结果
return dfs(cards)
- C语言版
// 目标值
#define target 24.0
// 误差
#define deviation 1e-6
// 四种运算,注意加和乘放在前两位,因为这两种运算满足交换律,方便后续处理
#define add 0
#define mul 1
#define sub 2
#define div 3
// 使用回溯判断满足运算结果为24的情况
bool dfs(double* cards, int size)
{
// 边界条件
if (size == 0)
{
return false;
}
// 返回结果
if (size == 1)
{
return fabs(cards[0] - target) < deviation;
}
// 任取数组中的两个数
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
// 取得数不能相同
if (i != j)
{
// 将剩下的数存到新的数组中
double* newCards = (double*)malloc(sizeof(double) * (size - 1));
int newCardsIndex = 0;
for (int k = 0; k < size; k++)
{
if (k != i && k != j)
{
newCards[newCardsIndex++] = cards[k];
}
}
// 开始根据四种运算对最外层循环取出的两个数进行运算并将运算结果放到新的数组中
double x = cards[i];
double y = cards[j];
for (int l = 0; l < 4; l++)
{
// 剪枝,满足交换律的运算(比如加和乘)只运算一次,提高效率
if (l < 2 && i > j)
{
continue;
}
if (l == add)
{
newCards[newCardsIndex] = x + y;
}
else if (l == mul)
{
newCards[newCardsIndex] = x * y;
}
else if (l == sub)
{
newCards[newCardsIndex] = x - y;
}
else if (l == div)
{
if (fabs(y) < deviation)
{
continue;
}
newCards[newCardsIndex] = x / y;
}
// 根据当前运算结果进行下一次运算,并隐含回溯,开始使用下一种运算符进行运算
if (dfs(newCards, newCardsIndex + 1))
{
free(newCards);
return true;
}
}
}
}
}
// 所有情况都没满足
return false;
}
bool judgePoint24(int* cards, int cardsSize)
{
// 将int数组转成double类型的数组
double* cardList = (double*)malloc(sizeof(double) * cardsSize);
for (int i = 0; i < cardsSize; i++)
{
cardList[i] = (double)cards[i];
}
// 返回最终结果
bool res = dfs(cardList, cardsSize);
free(cardList);
return res;
}
十【提交结果】
-
Java语言版
-
Python语言版
-
C语言版