//模板    
public static void dfs(){
    if(当前状态==目标状态){
        //逻辑处理
        return  ;
    }
    for(寻找新状态){
        if(合法){
            dfs(新状态);
        }
    }
}

回溯:

向前每一步,标记,不行就回退,并撤销状态

//模板    
public static void dfs(){
    if(当前状态==目标状态){
        //逻辑处理
        return  ;
    }
    for(寻找新状态){
        if(合法){
            //标记状态
            dfs(新状态);
            撤销状态;
        }
    }
}

人类的开心程度有高低之分,数字也一样。

给定一个正整数 ,在  的数位之间插入  个加号,使其变成一个表达式,计算得出的结果就是  的一个  级开心程度。

例如  时,我们可以往  和  之间插入一个  号,使其变为 ,计算出结果为  。那么  就是  的一个  级开心程度。

给定 ,请你计算出  的  级开心程度的最大值与最小值之差。

输入格式

一行输入两个正整数 n,含义见题面。

输出格式

一行一个整数,表示  的  级开心程度的最大值与最小值之差。


import java.util.Scanner;

public class Startup {
    static long max = Long.MIN_VALUE;
    static long min = Long.MAX_VALUE;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        char[] c = sc.next().toCharArray();
        int k = sc.nextInt();
        StringBuilder str = new StringBuilder();
        dfs(0, c, k, str);
        System.out.println(max - min);
    }

    public static void dfs(int u, char[] c, int k, StringBuilder str) {
        if (u == c.length) {
            if (k == 0) {
                String[] s = str.toString().split("\\+");
                long res = 0;
                for (String x : s) {
                    res += Long.valueOf(x);
                }
                max = Math.max(max, res);
                min = Math.min(min, res);
            }
            return ;
        }

        // 递归添加当前字符,不加 '+' 符号
        str.append(c[u]);
        dfs(u + 1, c, k, str);
        str.deleteCharAt(str.length() - 1);  // 回溯

        // 如果k大于0并且可以加'+',则递归添加当前字符并加上'+'符号
        if (k > 0 && u < c.length - 1) {
            str.append(c[u]);
            str.append('+');
            dfs(u + 1, c, k - 1, str);
            str.deleteCharAt(str.length() - 1);
            str.deleteCharAt(str.length()-1);// 回溯删除最后的'+'
        }
    }
}

路径之谜

小明冒充 X 星球的骑士,进入了一个奇怪的城堡。

城堡里边什么都没有,只有方形石头铺成的地面。

假设城堡地面是 n×n 个方格。如下图所示。

图1

按习俗,骑士要从西北角走到东南角。可以横向或纵向移动,但不能斜着走,也不能跳跃。每走到一个新方格,就要向正北方和正西方各射一箭。(城堡的西墙和北墙内各有 n 个靶子)同一个方格只允许经过一次。但不必走完所有的方格。如果只给出靶子上箭的数目,你能推断出骑士的行走路线吗?有时是可以的,比如上图中的例子。

本题的要求就是已知箭靶数字,求骑士的行走路径(测试数据保证路径唯一)

输入描述

第一行一个整数 N (0≤N≤20),表示地面有 N×N 个方格。

第二行 N 个整数,空格分开,表示北边的箭靶上的数字(自西向东)

第三行 N 个整数,空格分开,表示西边的箭靶上的数字(自北向南)

 

import java.util.*;
public class Main{
    static int[] col;
    static int[] row;
    static boolean[][] st;
    static ArrayList<Integer> arr;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        col=new int[n];
        row=new int[n];
        for(int i=0;i<n;i++) col[i]=sc.nextInt();//横
        for(int j=0;j<n;j++) row[j]=sc.nextInt();//竖
        st=new boolean[n][n];
        arr=new ArrayList<>();
        arr.add(0);
        col[0]--;
        row[0]--;
        st[0][0]=true;
        dfs(0,0,n);
        for(int i=0;i<arr.size();i++){
            System.out.print(arr.get(i)+" ");
        }
    }
    public static boolean dfs(int x,int y,int n){
        //最终的结果判断
        if(x==n-1&&y==n-1){
            for(int i=0;i<n;i++){
                if(col[i]!=0||row[i]!=0) return false;
            }
            //如果通过了上面的判断,说明符合条件,最终找到了路径,返回true
            return true;
        }
        //一整个搜索的过程
        int[] dx={1,-1,0,0};
        int[] dy={0,0,1,-1};
        for(int i=0;i<4;i++){
            int x1=x+dx[i];
            int y1=y+dy[i];
            //判断不满足的情况并排除,有点像剪枝
            if(x1<0||x1>=n||y1<0||y1>=n||st[x1][y1]) continue;
            if(row[x1]<=0||col[y1]<=0) continue;
            //能走到这一步说明是合法的一个点,改变,更新相关的值
            st[x1][y1]=true;
            row[x1]--;
            col[y1]--;
            arr.add(x1*n+y1);
            //如果在这个点的深度搜索之后,得到了最终的路径就返回true,否则就回溯
            if(dfs(x1,y1,n)) return true;
            st[x1][y1]=false;
            row[x1]++;
            col[y1]++;
            arr.remove(arr.size()-1);

        }
        return false;
    }
}

回溯:

  1. 回溯的需求

    • 递归搜索:回溯通过递归的方式,尝试从当前位置 (x, y) 向四个方向(上下左右)探索。如果当前路径符合所有的约束条件,继续往下走;如果不符合,撤回(回溯)到上一个位置,重新尝试其他路径。
    • 状态空间的探索:回溯可以有效地探索所有可能的路径。通过在每一步递归时不断选择不同的方向,程序能够遍历所有的路径组合,直到找到一条符合条件的路径,或者确认没有符合条件的路径。
    • 回溯的撤销操作:每次递归深入一层后,都会记录当前的选择,并更新状态(例如标记当前的位置已访问,并更新行列的可访问次数)。当递归返回时,程序需要撤销这些操作,恢复到之前的状态,从而尝试新的路径。

回溯的实现:

1. 递归进入

  • 通过 dfs(x, y, n) 方法来进行递归搜索路径。
  • 如果当前的位置 (x, y) 是目标位置 (n-1, n-1),程序会检查是否所有的行和列都已经按照要求被访问了。如果所有要求满足,返回 true,表示找到了一条符合要求的路径。
  • 如果当前位置不是目标位置,程序会继续尝试在四个方向上走。

2. 四个方向的选择

  • 程序通过 dx 和 dy 数组定义了四个方向的相对坐标变化(右、左、下、上)。
  • 对于每个方向,程序会判断:
    • 是否越界。
    • 是否已经访问过该位置。
    • 是否还可以访问该位置(即该位置所在的行列是否还有可用的访问次数)。

3. 选择路径

  • 如果可以选择某个位置作为下一个步骤,程序会标记该位置已访问,并更新行列的可用次数。
  • 然后递归地进入这个新的位置,继续搜索下一个位置。

4. 回溯(撤销操作)

  • 如果在某个方向尝试失败(即没有找到符合条件的路径),程序会撤销这一步的选择:
    • 恢复该位置的访问状态,即将 st[x1][y1] 恢复为 false
    • 恢复该位置所在行列的可用访问次数。
    • 从路径中移除当前的位置。
  • 这样,程序可以回到上一个位置,继续尝试其他可能的方向。

为什么要回溯:

  • 路径选择的可变性:在探索路径时,每个位置可以选择的方向很多,而每个方向又有不同的条件限制(例如行列的可用次数、是否越界、是否已访问等)。这些条件和选择导致了路径的巨大可变性,而回溯恰好适合这种需要探索多个路径的情境。
  • 符合条件的路径很少:在大多数情况下,路径并不直接就能符合所有的约束条件(比如每行每列访问的次数、是否越界等),需要在遍历过程中不断地尝试不同的路径,直到找到一条符合条件的路径为止。
  • 撤销操作的必要性:如果当前路径选择不符合条件,必须撤销当前选择并回到上一个状态,重新选择其他路径。回溯正是通过这种撤销操作来处理无解情况或寻找其他解的过程。
posted on 2025-03-12 08:36  fafrkvit  阅读(42)  评论(1)    收藏  举报