Java实现搜索回溯经典题目

157 篇文章 6 订阅
154 篇文章 5 订阅

前言

  • 搜索与回溯是计算机解题中常用的算法,很多问题无法根据某种确定的计算法则来求解,可以利用搜索与回溯的技术求解。回溯是搜索算法中的一种控制策略。它的基本思想是:为了求得问题的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索,如此反复进行,直至得到解或证明无解。

  • 如迷宫问题:进入迷宫后,先随意选择一个前进方向,一步步向前试探前进,如果碰到死胡同,说明前进方向已无路可走,这时,首先看其它方向是否还有路可走,如果有路可走,则沿该方向再向前试探;如果已无路可走,则返回一步,再看其它方向是否还有路可走;如果有路可走,则沿该方向再向前试探。按此原则不断搜索回溯再搜索,直到找到新的出路或从原路返回入口处无解为止。


搜索框架

搜索伪代码/公式
基本上所有的搜索与回溯都是这个公式的变种

void Search(int k)
{
 for (i=1;i<=算符种数;i++)
  if (满足条件)
     {
    保存结果
    if (到目的地) 输出解;
     else Search(k+1);
    恢复:保存结果之前的状态{回溯一步}
     }
}


经典问题

【问题1】八皇后问题

  • 题目
    八皇后问题:要在国际象棋棋盘中放八个皇后,使任意两个皇后都不能互相吃。(提示:皇后能吃同一行、同一列、同一对角线的任意棋子。)

  • 分析
    放置第i个(行)皇后的算法为:
    int search(i);
    {
       int j;
       for (第i个皇后的位置j=1;j<=8;j++ ) //在本行的8列中去试
       if (本行本列允许放置皇后)
        {
         放置第i个皇后;
    对放置皇后的位置进行标记;
         if (i==8) 输出 //已经放完个皇后
           else search(i+1); //放置第i+1个皇后
         对放置皇后的位置释放标记,尝试下一个位置是否可行;
        }
    }

  • 参考代码

public class EightQueue {
    /**
     * 存放每一行,皇后的位置,a[1]=6,代表第一行的皇后放在位置6
     * */
    int[] a = new int[9];
    /**
     * 每一列,是否被放置了皇后
     * */
    int[] b = new int[9];
    /**
     * 左对角线是否放置了皇后
     * */
    int[] c = new int[17];
    /**
     * 右对角线是否放置了皇后
     * */
    int[] d = new int[17];
    /**
     * 解法的个数
     * */
    int num = 0;

    public void search(int n) {
        // 算符种类
        for (int i=1; i<=8; i++) {
            // 判断是否可以放棋子
            if(b[i]==0 && c[n+i]==0 && d[n-i+8]==0) {
                // 保存条件
                a[n] = i;
                b[i] = 1;
                c[n+i] = 1;
                d[n-i+8] = 1;
                // 目的地
                if (n == 8) {
                    print(a);
                    num ++;
                } else {
                    search(n + 1);
                }
                // 回溯
                b[i] = 0;
                c[n+i] = 0;
                d[n-i+8] = 0;
            }
        }
    }

    public void print(int[] a) {
        for (int i=1; i<a.length; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println("");
    }

    public static void main(String[] args) {
        EightQueue e = new EightQueue();
        e.search(1);
        System.out.println("解法个数 " + e.num);//92个解
    }
}

【问题2】自然数分解

任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和

当n=7共14种拆分方法:
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
total=14

【代码实现】

/**
 * @author 谢世杰
 */
public class SplitData {
    int[] a;
    int goal;

    /**
     * 任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和
     * 算符种类:要分割的自然数n
     * 结果数组:a[n],存储这分割的结果
     * */
    void search(int n, int t) {
        // 算符种类,从1开始,一直到n
        for (int i=a[t-1]; i<=n; i++) {
            // 满足条件,把分解的数字存入数组并在n基础上减去
            // 当前数i要大于等于前1位数,且不过n
            if(i < goal) {
                a[t] = i;
                n -= i;
                if (n == 0) {
                    print(t);
                } else {
                    search(n, t+1);
                }
                n += i;
            }
        }
    }

    public void print(int t) {
        System.out.println("结果为:");
        for (int i=1; i <= t; i++) {
            System.out.print(a[i] + " ");
        }
        System.out.println("");
    }

    public static void main(String[] args) {
        SplitData s = new SplitData();
        // 想要分解的自然数
        int n = 7;
        s.a = new int[n+1];
        for (int i=0; i<s.a.length; i++) {
            s.a[i] = 1;
        }
        s.goal = n;
        s.search(n,1);
    }
}

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员世杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值