LeetCode 679. 24 点游戏

题目描述

679. 24 点游戏

思路分析

解法一:全排列 + 回溯枚举运算符(推荐)

核心思路

  • 4 张牌共有 4! = 24 种排列,每两个数之间有 4 种运算(+、-、×、÷),共 4³ = 64 种运算符组合。
  • 枚举所有 4! × 4³ = 1536 种情况,对每种情况按固定括号优先级依次计算。
  • 但括号的结合方式也影响结果,例如 (a op b) op (c op d)a op (b op (c op d)) 不同。
  • 因此使用递归回溯:每次从当前数列中选取两个数,用某种运算合并为一个新数,递归处理剩余数字,直到只剩一个数为止。
  • 判断最终结果是否等于 24(使用浮点数误差 1e-6 比较)。

步骤

  1. 将 4 个整数转为浮点数列表。
  2. 每次从列表中选取两个数 ab(有序,枚举所有下标对)。
  3. 枚举 4 种运算(+、-、×、÷),注意除数不为零时才进行除法。
  4. 将运算结果加入列表,移除 ab,递归处理新列表。
  5. 递归终止条件:列表只剩一个数,判断是否约等于 24。


复杂度分析

  • 时间复杂度:O(1),数字规模固定为 4,枚举总量为常数 1536 种情况。
  • 空间复杂度:O(1),递归深度最多为 4 层,为常数级别。
class Solution {

    // 浮点比较精度阈值
    private static final double EPS = 1e-6;

    public boolean judgePoint24(int[] cards) {
        List<Double> nums = new ArrayList<>();
        for (int card : cards) {
            nums.add((double) card);
        }
        return backtrack(nums);
    }

    private boolean backtrack(List<Double> nums) {
        if (nums.size() == 1) {
            // 判断最终结果是否等于 24
            return Math.abs(nums.get(0) - 24.0) < EPS;
        }

        int n = nums.size();
        // 枚举所有两个数的有序对 (i, j)
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (i == j) {
                    continue;
                }
                double a = nums.get(i);
                double b = nums.get(j);

                // 构建去除 i、j 之后的剩余列表
                List<Double> next = new ArrayList<>();
                for (int k = 0; k < n; k++) {
                    if (k != i && k != j) {
                        next.add(nums.get(k));
                    }
                }

                // 枚举 4 种运算符,将 a op b 的结果加入列表
                for (double result : compute(a, b)) {
                    next.add(result);
                    if (backtrack(next)) {
                        return true;
                    }
                    next.remove(next.size() - 1);
                }
            }
        }
        return false;
    }

    // 返回 a 和 b 所有合法运算结果
    private List<Double> compute(double a, double b) {
        List<Double> results = new ArrayList<>();
        results.add(a + b);
        results.add(a - b);
        results.add(a * b);
        // 除数不为零才进行除法
        if (Math.abs(b) > EPS) {
            results.add(a / b);
        }
        return results;
    }
}
const eps = 1e-6

func judgePoint24(cards []int) bool {
	nums := make([]float64, len(cards))
	for i, card := range cards {
		nums[i] = float64(card)
	}
	return backtrack(nums)
}

func backtrack(nums []float64) bool {
	if len(nums) == 1 {
		// 判断最终结果是否等于 24
		return math.Abs(nums[0]-24.0) < eps
	}

	n := len(nums)
	// 枚举所有两个数的有序对 (i, j)
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			if i == j {
				continue
			}
			a, b := nums[i], nums[j]

			// 构建去除 i、j 之后的剩余列表
			next := make([]float64, 0, n-1)
			for k := 0; k < n; k++ {
				if k != i && k != j {
					next = append(next, nums[k])
				}
			}

			// 枚举 4 种运算符
			for _, result := range compute(a, b) {
				next = append(next, result)
				if backtrack(next) {
					return true
				}
				next = next[:len(next)-1]
			}
		}
	}
	return false
}

// compute 返回 a 和 b 所有合法运算结果
func compute(a, b float64) []float64 {
	results := []float64{a + b, a - b, a * b}
	// 除数不为零才进行除法
	if math.Abs(b) > eps {
		results = append(results, a/b)
	}
	return results
}

相似题目

题目 难度 考察点
46. 全排列 中等 回溯、全排列
47. 全排列 II 中等 回溯、去重
282. 给表达式添加运算符 困难 回溯、表达式求值
241. 为运算表达式设计优先级 中等 分治、回溯
932. 漂亮数组 中等 分治、数学
39. 组合总和 中等 回溯、枚举
51. N 皇后 困难 回溯、枚举
本文作者:
本文链接: https://hgnulb.github.io/blog/2025/18803396
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!