深度优先搜索算法求解TSP问题(搜索算法)

【问题描述】采用深度优先搜索算法求解TSP问题,并在搜索过程中,使用界限条件(当前结点已经走过的路径长度要小于已求得的最短路径)进行“剪枝”操作(不再对后续结点进行遍历),从而提高搜索效率。采用queue模块中的栈(LifoQueue)来实现深度优先搜索。

【输入形式】在屏幕上输入顶点个数和连接顶点间的边的邻接矩阵,边上的权可能有小数点。

【输出形式】在整个算法过程中的先后搜索路径(最多输出20次最先搜索的路径),最优值和其中一条最优路径。

【样例1输入】

4

0 30 6 4

30 0 5 10

6 5 0 20

4 10 20 0

【样例1输出】

1

1->2

1->2->3

1->2->3->4

1->2->4

1->3

1->3->2

1->3->2->4

1->3->4

1->4

1->4->2

1->4->2->3

1->4->3

25: 1->3->2->4

【样例说明】

 输入:顶点个数为4。连接顶点间边的邻接矩阵大小为4行4列,位置[i,j]上元素值表示第i个顶点到第j个顶点的距离,0表示两个顶点间没有边连接。

 输出:在整个算法过程中的先后搜索路径,最优值为25,最优路径为:1->3->2->4。

【评分标准】根据输入得到准确的输出。

带有详细注释的python版代码:

from queue import LifoQueue  # 使用LifoQueue实现深度优先搜索

n = int(input())  # 输入节点数
maze = [[v if v else float('inf') for v in list(map(eval, input().split()))] for i in range(n)]  # 输入邻接矩阵


class Node:  # 解空间的一个状态,此方法好处是思路清晰,坏处是空间消耗大
    def __init__(self, cost=0, path=None, visited=None):  # cost为当前路径的总长度,path记录当前路径,visited为已访问的节点
        self.cost = cost  # 从0开始编号
        self.path = path if path else []  # path中存储的是节点的编号减1
        self.visited = visited if visited else [0] * n  # visited中存储的是节点的编号减1位置是否被访问过

    def add_vertex(self, vertex):   # 添加一个节点,并在一个独立的新对象中返回新的状态
        new_node = Node(self.cost, self.path.copy(), self.visited.copy())  # (copy.deepcopy会超时,具体原因不明)
        if new_node.path:  # 如果不是第一个节点
            new_node.cost += maze[new_node.path[-1]][vertex]  # 更新cost
        new_node.visited[vertex] = 1  # 更新visited
        new_node.path.append(vertex)  # 更新path
        return new_node  # 返回新的状态结点

    def nexts(self):  # 返回当前状态下的所有下一状态(此处不包含剪枝判断)
        return [i for i in range(n) if self.visited[i] == 0]  # 返回当前状态下的所有未访问的节点

    def __repr__(self):  # 重写__repr__方法,使得print可直接输出
        return '->'.join([str(i + 1) for i in self.path])  # 输出当前路径


best_cost = float('inf')  # 最优解,初始值无穷大
best_node = None  # 最优解对应的状态结点
count = 20  # 输出前20个途中的路径
open = LifoQueue()  # 使用LifoQueue创建open表(其实不需要close表,因为每个状态都是独立的)
open.put(Node().add_vertex(0))  # 将初始状态加入open表
while not open.empty():  # 当open表不为空时
    cur = open.get()  # 取出open表中的一个状态
    if count > 0:  # 输出前20个途中的路径
        print(cur)  # 输出当前状态
        count -= 1  # 可输出个数减1
    if cur.cost >= best_cost:  # 剪枝,如果当前状态的cost大于等于最优解,则不需要再搜索下去
        continue  # 跳过当前状态
    if len(cur.path) == n:  # 如果当前状态已经访问了所有节点
        if cur.cost + maze[cur.path[-1]][0] < best_cost:  # 如果当前状态的cost加上回到起点的cost小于最优解
            best_cost = cur.cost + maze[cur.path[-1]][0]  # 更新最优解
            best_node = cur  # 更新最优解对应的状态结点
    else:  # 如果当前状态没有访问所有节点
        for next in cur.nexts()[::-1]:  # 逆序遍历当前状态的所有下一状态
            node = cur.add_vertex(next)  # 添加一个节点,并在一个独立的新对象中返回新的状态
            if node.cost < best_cost:  # 如果新的状态的cost小于最优解
                open.put(node)  # 将新的状态加入open表
print('%.3f' % best_cost if type(best_cost) == float else best_cost, ': ', best_node, sep='')  # 输出最优解,如果是浮点数则保留3位小数


C++版代码:

#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<iomanip>
typedef double ll;
using namespace std;

int n;
ll maze[10][10];

class Node {
public:
	Node add_vertex(int vertex) {
		auto node = new Node(*this);
		if (!node->path.empty())
			node->cost += maze[node->path.back()][vertex];
		node->visited[vertex] = 1;
		node->path.emplace_back(vertex);
		return *node;
	}
	vector<int> nexts() {
		vector<int> nexts;
		for (int i = 0; i < n; i++)
			if (!visited[i])
				nexts.emplace_back(i);
		return nexts;
	}
	ll cost = 0;
	vector<int> path = vector<int>();
	vector<int> visited = vector<int>(n);
};

ostream& operator<<(ostream& os, const Node& node) {
	os << node.path[0] + 1;
	for (size_t i = 1; i < node.path.size(); i++)
		os << "->" << node.path[i] + 1;
	return os;
}


int main() {
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++) {
			cin >> maze[i][j];
			if (maze[i][j] == 0) {
				maze[i][j] = ll(0xffffffffffffffff);
			}
		}
	ll best_cost = ll(0xffffffffffffffff);
	Node best_node;
	int count = 20;
	stack<Node> open = stack<Node>();
	open.push(Node().add_vertex(0));
	while (!open.empty()) {
		Node cur = open.top();
		open.pop();
		if (count > 0) {
			cout << cur << endl;
			count -= 1;
		}
		if (cur.cost >= best_cost)
			continue;
		if (cur.path.size() == size_t(n)) {
			if (cur.cost + maze[cur.path.back()][0] < best_cost) {
				best_cost = cur.cost + maze[cur.path.back()][0];
				best_node = cur;
			}
		}
		else {
			auto nexts = cur.nexts();
			for (int i = nexts.size() - 1; i >= 0; i--) {
				auto node = cur.add_vertex(nexts[i]);
				if (node.cost < best_cost)
					open.push(node);
			}
		}
	}
	if (fabs(int(best_cost) - best_cost) < 1e-7)
		cout << best_cost << ": " << best_node;
	else
		cout << fixed << setprecision(3) << best_cost << ": " << best_node;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/ik666/article/details/129215152