使用DFS求解“24点游戏算法”

1.题目

问题描述:
给出4个1-10的数字,通过加减乘除,得到数字为24就算胜利
输入:
4个1-10的数字。[数字允许重复,但每个数字仅允许使用一次,测试用例保证无异常数字]
输出:
true or false

示例:
输入:

7 2 1 10

输出

true

2.思路精要:

  • (1)依次选择n[0],n[1],n[2],n[3]作为顶点。
  • (2)某次遍历后,如果最后节点的值为24则返回true,否则回溯。
  • (3)所有遍历后均得不到24,返回false。

3. 该题的图到底长什么样呢?

经过分析,如下图所示:

注意;图中的标注为各个顶点的初始值,遍历过程中顶点的值会发生变化。如按加法且路径为左上角-->右上角-->右下角-->左下角,则左下角顶点的值为n[0]+n[1]+n[2]+n[3]。因此,一次遍历后,最后节点的值等于24,就意味着可以得到24。

4.具体代码

// package org.jzy.huawei108;

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            // 初始化数字数组和标志数组
            int[] nums = new int[4];
            int[] signs = new int[4];
            for (int i = 0; i < nums.length; i++) {
                nums[i] = scanner.nextInt();
            }

            boolean can = false; // 是否能得到24
            for (int i = 0; i < nums.length; i++) {
                signs[i] = 1;
                if (dfs(nums, signs, nums[i], 24)) {
                    can = true;
                    break;
                }
                signs[i] = 0;
            }
            System.out.println(can);// 输出结果
        }
    }

    /**
     * 深度优先算法逻辑
     *
     * @param nums     输入的4个数字
     * @param signs    访问标志数组
     * @param v        顶点的值
     * @param required 需要通过四则运算得到的数字
     * @return
     */
    private static boolean dfs(int[] nums, int[] signs, int v, int required) {
        boolean allVisited = true;// 四个角均被访问
        for (int sign : signs) {
            if (sign == 0) {
                allVisited = false;
            }
        }

        if (allVisited) {
            return v == required; // 在所有节点均被访问的前提下,判断最后的结果是否为所需要的结果。
        }

        // 访问所有的邻接顶点
        for (int i = 0; i < nums.length; i++) {
            if (signs[i] == 0) {
                signs[i] = 1;
                if (dfs(nums, signs, v + nums[i], required) // 加法
                        || dfs(nums, signs, v - nums[i], required) // 减法
                        || dfs(nums, signs, v * nums[i], required) // 乘法
                        || nums[i] != 0 && v % nums[i] == 0 && dfs(nums, signs, v / nums[i], required)/* 除法 */) {
                    return true;// 如果可以通过四则运算得到需要的结果,则返回true。
                }
                signs[i] = 0; // 如果不能通过四则运算得到,则进行回溯。
            }
        }

        return false;//当所有情况均得不到需要的结果,返回false。
    }
}