数据结构与算法之限界搜索剪枝

限界搜索剪枝原理又被称为剪枝算法,是一种优化搜索算法的技术。其基本思想是在搜索过程中,根据问题的性质和约束,尽可能地减少搜索空间,避免不必要的搜索,从而提高搜索效率。

限界搜索剪枝原理的核心是设置搜索的上限和下限,即搜索的“限界”。通常情况下,我们可以通过下列几种方式来达到剪枝的效果:

1.剪除不合法的状态:在搜索的过程中,如果某个状态不符合问题的约束条件,那么就可以直接将其剪枝。

2.剪除重复的状态:在搜索的过程中,如果出现了重复的状态,那么就可以直接将其剪枝。

3.剪除无效的状态:在搜索的过程中,如果某个状态已经被证明无解,那么就可以直接将其剪枝。

通过这些方式,我们可以大大减少搜索的时间和空间,提高搜索效率。

在这里插入图片描述



一、C 限界搜索剪枝 源码实现及详解

限界搜索是一种搜素算法,其主要思想是在搜索的过程中,通过限制搜索范围,减少不必要的搜索次数,从而提高搜索效率。

在限界搜索过程中,我们需要进行剪枝操作,即在搜索的过程中,判断当前搜索的状态是否还有可能成为最优解,如果不可能,则直接剪枝,避免继续搜索下去,从而提高搜索效率。

下面是 C++ 实现的限界搜索剪枝:

#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>

using namespace std;

// 定义状态结构体
struct State {
    int x, y;  // 当前节点坐标
    int step;  // 当前走的步数
    int bound; // 当前状态的下界
};

const int MAXN = 100;  // 地图最大尺寸
int map[MAXN][MAXN];   // 地图
bool vis[MAXN][MAXN];  // 标记当前节点是否已经访问过
int n;                 // 地图尺寸
int sx, sy, ex, ey;    // 起点和终点坐标

// 计算当前状态的下界
int getBound(int x, int y) {
    int maxDist = max(abs(ex - x), abs(ey - y)); // 计算到终点的最大距离
    int remainStep = n * n - 1 - (n - 1 - x) * n - (n - 1 - y); // 剩余步数
    if (remainStep < maxDist) {
        return -1;  // 若剩余步数不足以到达终点,则返回-1表示当前状态不可行
    }
    return maxDist; // 否则返回下界
}

// 限界搜索
int bfs() {
    // 定义状态队列
    priority_queue<State, vector<State>, function<bool(State, State)>> q([](State a, State b) {
        // 自定义优先级队列的比较函数,按照bound从小到大排序
        return a.bound > b.bound;
    });

    // 初始化起点状态
    State start;
    start.x = sx, start.y = sy;
    start.step = 0;
    start.bound = getBound(sx, sy);
    vis[sx][sy] = true;
    q.push(start);

    while (!q.empty()) {
        // 取出队首状态
        State cur = q.top();
        q.pop();

        // 判断是否到达终点
        if (cur.x == ex && cur.y == ey) {
            return cur.step;
        }

        // 遍历当前状态的所有可达状态
        for (int i = -1; i <= 1; ++i) {
            for (int j = -1; j <= 1; ++j) {
                if (i == 0 && j == 0) continue;  // 不考虑原地不动的情况
                int nx = cur.x + i, ny = cur.y + j;
                if (nx < 0 || nx >= n || ny < 0 || ny >= n) continue;  // 判断是否越界
                if (vis[nx][ny]) continue;  // 判断是否已经访问过
                if (map[nx][ny] == 0) continue;  // 判断是否为障碍物
                // 计算下一个状态的下界
                int nBound = getBound(nx, ny);
                if (nBound == -1) continue;  // 判断下界是否可行
                // 新建下一个状态
                State next;
                next.x = nx, next.y = ny;
                next.step = cur.step + 1;
                next.bound = nBound;
                vis[nx][ny] = true;
                q.push(next); // 将下一个状态加入队列
            }
        }
    }
    return -1;  // 没有到达终点,返回-1表示无解
}

int main() {
    // 读入地图尺寸和起点终点坐标
    cin >> n >> sx >> sy >> ex >> ey;
    // 读入地图信息
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            cin >> map[i][j];
        }
    }
    // 搜索并输出结果
    int res = bfs();
    if (res == -1) {
        cout << "No solution" << endl;
    } else {
        cout << res << endl;
    }
    return 0;
}

在实现中,我们首先定义了一个状态结构体,包含了当前节点坐标、当前走的步数以及当前状态的下界。

在搜索过程中,我们定义了一个优先级队列,按照当前状态的下界从小到大排序。每次取出队首状态时,也就是当前下界最小的状态,然后遍历其所有可达状态,计算可达状态的下界,若下界不可行,则剪枝;否则将其加入队列中。在遍历完所有可达状态后,继续从队列中取出下一状态进行搜索,直到到达终点或队列为空为止。

在计算状态下界时,我们需要计算当前节点到终点的最大距离,以及剩余步数。若剩余步数不足以到达终点,则返回-1表示当前状态不可行。否则,返回当前节点到终点的最大距离作为当前状态的下界,用于优先级队列的排序。

在实现的过程中,我们还需要注意细节问题,如起点和终点不能为障碍物、不考虑原地不动等。

限界搜索剪枝算法可以用于解决各种搜索问题,在一些经典问题中,如旅行商问题、最小生成树问题等,限界搜索已经成为了关键算法。

在这里插入图片描述



二、C++ 限界搜索剪枝 源码实现及详解

C++ 限界搜索剪枝是一种搜索算法,它通过限制搜索空间和剪枝不必要的节点来提高搜索效率。在这里,我们将介绍 C++ 限界搜索剪枝的源码实现及详解。

一、限界搜索剪枝的基本思想

限界搜索剪枝是一种深度优先搜索算法,它的基本思想是在搜索树中,对于当前搜索到的节点,计算其可能的上下界,如果其上下界之间的搜索空间都被完全包含在已搜索的节点中,那么就可以剪枝,不必进一步搜索该节点的子节点。

二、限界搜索剪枝的实现步骤

  1. 定义搜索状态和搜索空间

首先,我们需要定义搜索状态和搜索空间。搜索状态是指在搜索过程中,在某个节点处所处的状态,通常用一个数组或结构体来表示。搜索空间是指搜索的状态集合,通常用队列或栈来实现。

代码示例:

struct State {
    // 状态数组
    // ...
    // 记录当前搜索状态信息的成员变量
};

queue<State> q; // 定义搜索空间
  1. 初始化搜索状态和搜索空间

接下来,我们需要初始化搜索状态和搜索空间。在初始化时,我们需要把初始状态加入到搜索空间中。

代码示例:

State init_state;
// 初始化搜索状态信息
q.push(init_state);
  1. 进行搜索

在搜索过程中,我们需要不断地从搜索空间中取出一个状态进行扩展,并利用限界剪枝来剪去不必要的节点。

代码示例:

while (!q.empty()) {
    State cur_state = q.front(); // 取出队首状态
    q.pop();

    if (cur_state.is_leaf()) {  // 检查是否为叶子节点
        // 处理叶子节点
        continue;
    }

    if (cur_state.is_infeasible()) {  // 检查是否为非可行解
        // 处理非可行解
        continue;
    }

    // 计算上下界,进行限界剪枝
    if (cur_state.is_pruned()) {
        // 处理剪枝节点
        continue;
    }

    // 扩展当前节点
    vector<State> next_states = cur_state.get_next_states(); // 获取当前节点的下一状态
    for (int i = 0; i < next_states.size(); i++) {
        q.push(next_states[i]);  // 将下一状态加入到搜索空间中
    }
}

三、限界搜索剪枝的实现技巧

  1. 利用贪心策略或动态规划来计算上下界

在进行限界搜索剪枝时,我们需要计算当前节点可能的上下界,以进行剪枝。而在计算上下界的过程中,我们可以利用贪心策略或动态规划来优化计算效率。

  1. 利用优先队列来优化搜索顺序

在搜索过程中,我们可以利用优先队列来优化搜索顺序,使得每次取出的状态都是优先级最高的。

  1. 利用哈希表来记录已搜索过的状态

在进行搜索时,我们需要记录已搜索过的状态,以免重复搜索相同的状态。为了优化记录效率,可以利用哈希表来记录已搜索过的状态。

四、总结

C++ 限界搜索剪枝是一种高效的搜索算法,它可以通过限制搜索空间和剪枝不必要的节点来提高搜索效率。在实现过程中,我们需要定义搜索状态和搜索空间,并利用贪心策略、优先队列和哈希表等技巧来实现剪枝,从而优化搜索效率。

在这里插入图片描述



三、java 限界搜索剪枝 源码实现及详解

限界搜索剪枝是一种搜索算法,它基于搜索树的广度优先搜索策略,但是在搜索过程中,它会维护一个上限或下限,从而避免搜索无用的分支。这种算法广泛应用于NP难问题的解决。

以下是Java中实现限界搜索剪枝的基本步骤:

  1. 定义一个状态类,该类包括问题的当前状态和其他必要的信息。

  2. 定义一个队列(或堆栈),用于存储待搜索的状态。

  3. 将初始状态加入队列中。

  4. 在循环中,从队列中取出下一个状态,并计算其所有可能的子状态。

  5. 对于每个子状态,如果它已经被处理过或者其状态不符合问题的要求,则忽略;否则,将其加入队列中。

  6. 在处理每个子状态时,通过计算当前状态已知的信息,得到该子状态的上限或下限。如果该上限或下限小于队列中已知的最佳解,则忽略该子状态。

  7. 如果队列为空,则搜索结束;否则,回到第4步继续处理队列中的下一个状态。

以下是一个简单的例子,它演示了如何使用限界搜索剪枝解决旅行商问题(TSP):

import java.util.*;

public class TSP {

    private static class State {
        int[] path;  // 旅行路径
        int numVisited;  // 已经访问的城市数
        int currCity;  // 当前所在的城市
        int lowerBound;  // 下限,即已经访问的城市的距离和
        int upperBound;  // 上限,即从当前城市开始的最小生成树
    }

    private static int[][] distance;  // 城市之间的距离

    public static int tsp(int[][] d) {
        distance = d;
        int n = distance.length;
        int[] path = new int[n];
        for (int i = 0; i < n; i++) {
            path[i] = i;
        }
        State initState = new State();
        initState.path = path;
        initState.numVisited = 1;
        initState.currCity = 0;
        initState.lowerBound = 0;
        initState.upperBound = getLowerBound(initState);
        Queue<State> queue = new PriorityQueue<>(Comparator.comparingInt(s -> s.upperBound));
        queue.offer(initState);
        int minDist = Integer.MAX_VALUE;
        while (!queue.isEmpty()) {
            State state = queue.poll();
            if (state.lowerBound >= minDist) {
                continue;  // 跳过不可能获得更优解的状态
            }
            if (state.numVisited == n) {
                int dist = state.lowerBound + distance[state.currCity][0];
                minDist = Math.min(minDist, dist);
            } else {
                int[] cities = state.path;
                for (int i = state.numVisited; i < n; i++) {
                    swap(cities, i, state.numVisited);
                    int newCity = cities[state.numVisited];
                    int dist = distance[state.currCity][newCity];
                    if (dist == 0) {  // 跳过相同城市和已处理过的城市
                        continue;
                    }
                    State newState = new State();
                    newState.path = cities.clone();
                    newState.numVisited = state.numVisited + 1;
                    newState.currCity = newCity;
                    newState.lowerBound = state.lowerBound + dist;
                    newState.upperBound = getLowerBound(newState);
                    queue.offer(newState);
                    swap(cities, i, state.numVisited);  // 回溯,恢复状态
                }
            }
        }
        return minDist;
    }

    private static int getLowerBound(State state) {
        int n = distance.length;
        int lb = state.lowerBound;
        int currCity = state.currCity;
        int[] cities = state.path;
        // 计算已经访问的城市的距离和
        for (int i = 1; i < state.numVisited; i++) {
            lb += distance[cities[i - 1]][cities[i]];
        }
        // 计算从当前城市出发的最小生成树
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        for (int i = state.numVisited; i < n; i++) {
            if (distance[currCity][cities[i]] > 0) {
                pq.offer(distance[currCity][cities[i]]);
            }
        }
        for (int i = state.numVisited; i < n && !pq.isEmpty(); i++) {
            int minDist = pq.poll();
            for (int j = i; j < n; j++) {
                if (distance[currCity][cities[j]] == minDist) {
                    swap(cities, i, j);
                    break;
                }
            }
            lb += minDist;
        }
        return lb;
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    public static void main(String[] args) {
        int[][] distance = {
                {0, 10, 15, 20},
                {10, 0, 35, 25},
                {15, 35, 0, 30},
                {20, 25, 30, 0}
        };
        int minDist = tsp(distance);
        System.out.println(minDist);  // 输出:80
    }
}

在这个例子中,我们首先定义了一个包含以下字段的State类:

  • path:旅行路径。
  • numVisited:已经访问的城市数。
  • currCity:当前所在的城市。
  • lowerBound:已经访问的城市的距离和。
  • upperBound:从当前城市开始的最小生成树。

我们使用一个优先级队列来按照上限排序所有的状态,每次从队列中取出下一个状态进行处理。在处理每个状态时,我们使用getLowerBound()方法计算其上限,如果该上限小于队列中已知的最佳解,则将该状态加入队列。如果队列为空,则说明已经完成查找,并返回找到的最佳解。

getLowerBound()方法的实现如下:

  1. 首先计算已经访问的城市的距离和。这样可以得到状态的下限,即已经确定的最小距离。

  2. 计算从当前城市出发的最小生成树。我们使用一个优先级队列来存储从当前城市出发到未访问城市的距离,每次取出最小距离即可。由于存在多个最小距离的情况,我们需要按照旅行路径的顺序来选择下一个城市。比如,如果当前城市是A,下一个城市是B或C,距离AB和AC相等,而旅行路径是ABC,则我们应该选择城市B。

在本例中,我们将初始状态加入了优先级队列中,所以我们可以直接开始处理状态。在处理每个状态时,我们根据当前状态计算其所有可能的子状态,然后根据子状态的上限来决定是否将其加入队列。如果上限小于队列中已知的最佳解,则忽略该子状态。如果队列为空,则说明已经完成查找,并返回找到的最佳解。

总的来说,限界搜索剪枝算法是一种高效的搜索算法,它可以帮助我们解决许多NP难问题。无论什么问题,都需要进行一些比较复杂的预处理,以计算下限和上限。最优的下限和上限的计算可以帮助我们优化算法的效率。

在这里插入图片描述






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值