From eaabe6553048f0a158b0b312626e29baf8398959 Mon Sep 17 00:00:00 2001 From: bettleChen <2207153529@qq.com> Date: Thu, 11 Jan 2024 09:22:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- X1.py | 6 + X2.py | 47 + X3.py | 282 +-- X4.py | 1464 ++++++++++++++ X4_1.py | 1459 ++++++++++++++ X4_2.py | 1615 +++++++++++++++ X4_3.py | 1730 +++++++++++++++++ X4_4.py | 1730 +++++++++++++++++ X5.py | 6 + background/10_红.png | Bin 0 -> 4705 bytes background/10_绿.png | Bin 0 -> 4865 bytes background/10_蓝.png | Bin 0 -> 4382 bytes background/1_红.png | Bin 0 -> 4510 bytes background/1_绿.png | Bin 0 -> 4653 bytes background/1_蓝.png | Bin 0 -> 4162 bytes background/2_红.png | Bin 0 -> 4661 bytes background/2_绿.png | Bin 0 -> 4823 bytes background/2_蓝.png | Bin 0 -> 4356 bytes background/3_红.png | Bin 0 -> 4707 bytes background/3_绿.png | Bin 0 -> 4905 bytes background/3_蓝.png | Bin 0 -> 4399 bytes background/4_红.png | Bin 0 -> 4565 bytes background/4_绿.png | Bin 0 -> 4725 bytes background/4_蓝.png | Bin 0 -> 4228 bytes background/5_红.png | Bin 0 -> 4666 bytes background/5_绿.png | Bin 0 -> 4857 bytes background/5_蓝.png | Bin 0 -> 4365 bytes background/6_红.png | Bin 0 -> 4739 bytes background/6_绿.png | Bin 0 -> 4908 bytes background/6_蓝.png | Bin 0 -> 4411 bytes background/7_红.png | Bin 0 -> 4578 bytes background/7_绿.png | Bin 0 -> 4747 bytes background/7_蓝.png | Bin 0 -> 4274 bytes background/8_红.png | Bin 0 -> 4738 bytes background/8_绿.png | Bin 0 -> 4908 bytes background/8_蓝.png | Bin 0 -> 4410 bytes background/9_红.png | Bin 0 -> 4714 bytes background/9_绿.png | Bin 0 -> 4913 bytes background/9_蓝.png | Bin 0 -> 4416 bytes background/减少节点.png | Bin 0 -> 2182 bytes background/删除节点.png | Bin 0 -> 2590 bytes .../判断路径是否为最短路径.png | Bin 0 -> 3431 bytes .../判断路径是否为贪心路径.png | Bin 0 -> 3416 bytes background/可达判断.png | Bin 0 -> 1725 bytes background/增加节点.png | Bin 0 -> 2140 bytes background/完全图.png | Bin 0 -> 1452 bytes background/撤销.png | Bin 0 -> 1361 bytes background/未选择.png | Bin 0 -> 1559 bytes background/清除路径.png | Bin 0 -> 2066 bytes background/矩形 2.png | Bin 0 -> 3295 bytes background/矩形.png | Bin 0 -> 997 bytes background/矩形2.png | Bin 0 -> 3295 bytes background/确认.png | Bin 0 -> 1236 bytes background/线路修改.png | Bin 0 -> 2301 bytes background/线路删除.png | Bin 0 -> 2265 bytes background/线路增加.png | Bin 0 -> 2176 bytes background/编组9.png | Bin 0 -> 4890 bytes background/编组_9.png | Bin 0 -> 15716 bytes background/背景.png | Bin 0 -> 93125 bytes demo.py | 36 + demo11.py | 21 + main.py | 309 +-- test/DP.py | 49 + test/Prin.py | 46 + test/demo1.py | 76 + test/demo10.py | 46 + test/demo11.py | 24 + test/demo12.py | 29 + test/demo13.py | 24 + test/demo2.py | 106 + test/demo3.py | 66 + test/demo5.py | 47 + test/demo6.py | 74 + test/demo7.py | 119 ++ test/demo8.py | 30 + test/demo9.py | 42 + test/dfs.py | 47 + test/dfs_1.py | 53 + test/greedy.py | 47 + 79 files changed, 9179 insertions(+), 451 deletions(-) create mode 100644 X1.py create mode 100644 X2.py create mode 100644 X4.py create mode 100644 X4_1.py create mode 100644 X4_2.py create mode 100644 X4_3.py create mode 100644 X4_4.py create mode 100644 X5.py create mode 100644 background/10_红.png create mode 100644 background/10_绿.png create mode 100644 background/10_蓝.png create mode 100644 background/1_红.png create mode 100644 background/1_绿.png create mode 100644 background/1_蓝.png create mode 100644 background/2_红.png create mode 100644 background/2_绿.png create mode 100644 background/2_蓝.png create mode 100644 background/3_红.png create mode 100644 background/3_绿.png create mode 100644 background/3_蓝.png create mode 100644 background/4_红.png create mode 100644 background/4_绿.png create mode 100644 background/4_蓝.png create mode 100644 background/5_红.png create mode 100644 background/5_绿.png create mode 100644 background/5_蓝.png create mode 100644 background/6_红.png create mode 100644 background/6_绿.png create mode 100644 background/6_蓝.png create mode 100644 background/7_红.png create mode 100644 background/7_绿.png create mode 100644 background/7_蓝.png create mode 100644 background/8_红.png create mode 100644 background/8_绿.png create mode 100644 background/8_蓝.png create mode 100644 background/9_红.png create mode 100644 background/9_绿.png create mode 100644 background/9_蓝.png create mode 100644 background/减少节点.png create mode 100644 background/删除节点.png create mode 100644 background/判断路径是否为最短路径.png create mode 100644 background/判断路径是否为贪心路径.png create mode 100644 background/可达判断.png create mode 100644 background/增加节点.png create mode 100644 background/完全图.png create mode 100644 background/撤销.png create mode 100644 background/未选择.png create mode 100644 background/清除路径.png create mode 100644 background/矩形 2.png create mode 100644 background/矩形.png create mode 100644 background/矩形2.png create mode 100644 background/确认.png create mode 100644 background/线路修改.png create mode 100644 background/线路删除.png create mode 100644 background/线路增加.png create mode 100644 background/编组9.png create mode 100644 background/编组_9.png create mode 100644 background/背景.png create mode 100644 demo.py create mode 100644 demo11.py create mode 100644 test/DP.py create mode 100644 test/Prin.py create mode 100644 test/demo1.py create mode 100644 test/demo10.py create mode 100644 test/demo11.py create mode 100644 test/demo12.py create mode 100644 test/demo13.py create mode 100644 test/demo2.py create mode 100644 test/demo3.py create mode 100644 test/demo5.py create mode 100644 test/demo6.py create mode 100644 test/demo7.py create mode 100644 test/demo8.py create mode 100644 test/demo9.py create mode 100644 test/dfs.py create mode 100644 test/dfs_1.py create mode 100644 test/greedy.py diff --git a/X1.py b/X1.py new file mode 100644 index 0000000..e3dd654 --- /dev/null +++ b/X1.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X1.py diff --git a/X2.py b/X2.py new file mode 100644 index 0000000..9192806 --- /dev/null +++ b/X2.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X2.py + +# encoding: utf-8 +# 第一题,讲二进制数转换为10进制数 +def binToDec_int(b): + d = 0 + for i in range(len(b)): + # 请在此添加代码,补全函数BinToDec_int() + # -----------Begin---------- + if b[i] == '1': + d = d + 2 ** (len(b) - 1 - i) + # ------------End----------- + return d + + +def binToDec_float(b): + # 请在此添加代码,补全函数BinToDec_int() + # -----------Begin---------- + d = 0.0 + for i in range(len(b)): + # 请在此添加代码,补全函数BinToDec_int() + # -----------Begin---------- + if b[i] == '1': + d = d + 2 ** (-(i+1)) + # ------------End----------- + return d + + +if __name__ == '__main__': + + tests = [] + inputlist = input() + # print(type(inputlist)) + # for i in inputlist: + # tests.append(str(i)) + tests = inputlist.split(',') + for num in tests: + if '.' in num: + num1 = num.split('.') + print(binToDec_int(num1[0])+binToDec_float(num1[1])) + else: + print(binToDec_int(num)) \ No newline at end of file diff --git a/X3.py b/X3.py index 6f20d55..197f61c 100644 --- a/X3.py +++ b/X3.py @@ -1,205 +1,81 @@ -# -*- encoding: utf-8 -*- -""" - @Author: packy945 - @FileName: X3.py - @DateTime: 2023/7/12 14:07 - @SoftWare: PyCharm -""" -# -*- encoding: utf-8 -*- -from head import * - -class Node: - # 标记节点按钮 - def node_mark_display(self): - for w in frame_right.winfo_children(): - w.destroy() - # 清除右侧组件 - curx = -1 - y0 = 0.1 - for i in d.Nodes: - if i[2] == 1: - curx = i[0] - lab = tk.Label(frame_right, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) - lab.place(relx=0.18, rely=0.02, width=200, height=30) - lab1 = tk.Label(frame_right, text=f"当前共有{len(d.Nodes)}个节点", bg=RED, fg=BLACK, font=('微软雅黑', 15)) - lab1.place(relx=0.1, rely=0.0 + y0, width=200, height=30) - lab1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) - if curx == -1: - text = "未选择" - else: - text = f"{d.Nodes[curx][1]}" - - lab2 = tk.Label(frame_right, text=text, bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) - lab1.place(relx=0.1, rely=0.05 + y0, width=80, height=30) - lab2.place(relx=0.4, rely=0.05 + y0, width=80, height=30) - B = tk.Button(frame_right, text="设置标签", command=lambda: self.node_rename()) - B.place(relx=0.7, rely=0.05 + y0, width=80, height=30) - B = tk.Button(frame_right, text="删除当前节点", command=lambda: self.node_del_exact()) - B.place(relx=0.1, rely=0.1 + y0, width=150, height=30) - if d.Nodes[curx][4] == 1: - text = '设为非必经节点' - else: - text = '设为必经节点' - B = tk.Button(frame_right, text=text, command=lambda: self.dominator(curx)) - B.place(relx=0.1, rely=0.15 + y0, width=150, height=30) - B = tk.Button(frame_right, text="增加一个节点", command=lambda: self.node_add()) - B.place(relx=0.1, rely=0.25 + y0, width=150, height=30) - B = tk.Button(frame_right, text="减少一个节点", command=lambda: self.node_del()) - B.place(relx=0.1, rely=0.3 + y0, width=150, height=30) - - def dominator(self, x): - d.Nodes[x][4] ^= 1 - G.draw() - self.node_mark_display() - - def node_mark(self, name): - if name >= len(d.Nodes): - self.node_404(name) - return - - for node in d.Nodes: - if node[0] == name: - node[2] = 1 +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X3.py + +from collections import Counter +··· +class Solve (): + def node_dfs(self): # 遍历 深度优先算法 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] + for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break else: - node[2] = 0 - G.draw() - self.node_mark_display() - - def node_del_exact(self): - if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 - tk.messagebox.askokcancel(title='错误', message='节点过少') - return - flag = 0 - cur = -1 - for node in d.Nodes:# 寻找当前选定的节点 - if node[2] == 1: - cur = node[0] - flag = 1 - if flag == 0: - tk.messagebox.askokcancel(title='错误', message='未选择节点') - return - d.nodes_num -= 1#节点数目减一 - d.ang = 360 / d.nodes_num#重新计算圆心角 - nodes = d.Nodes.copy()#复制节点 - d.Nodes.clear()#清除Data中的节点 - num = 0 - for n in nodes:#逐个复制节点 - if n[0] == cur:#若是删除的节点,则不复制 - continue - n[0] = num#重新编号 - rad = math.radians(num * d.ang)#计算当前角度 - x = int(math.cos(rad) * d.R)#计算当前点x坐标(相对) - y = int(math.sin(rad) * d.R)#计算当前点y坐标(相对) - n[3] = (d.center[0] + x, d.center[1] + y)#计算绝对坐标 - d.Nodes.append(n)#将当前点加入点集合中 - num += 1 - edges = d.Edges.copy()#将原有边集复制出来 - d.Edges.clear()#删除Data中的边集 - num = 0 - for e in edges:#复制边 - if e[1] == cur or e[2] == cur:#删除的边不复制 - continue - if e[1] > cur:#修改边对应的点的编号 - e[1] -= 1 - if e[2] > cur:#修改边对应的点的编号 - e[2] -= 1 - e[0] = num#重新编号 - d.Edges.append(e)#将边加入边集中 - num += 1 - G.draw()#重新绘图 - self.node_mark_display() - - def node_rename(self): - flag = 0 - for node in d.Nodes: - if node[2] == 1: - cur = node - flag = 1 - if flag == 1: - result = tkinter.simpledialog.askstring(title='修改节点标签', prompt='请输入修改后的标签:', - initialvalue=cur[1]) - else: - tk.messagebox.askokcancel(title='错误', message='未选择节点') - return - if result: - cur[1] = result - G.draw() - self.node_mark_display() - - def node_clear(self): - for node in d.Nodes: - node[2] = False - G.draw() - - - - def node_add(self): - # 增加一个节点 - nodes = len(d.Nodes) + 1 - # 如果节点数目大于五,则将连接详细信息改为不显示 - if nodes > 5: - d.edgeinfo = 0 - self.node_init(nodes) - G.draw() - self.node_mark_display() - - def node_set(self): - node = tkinter.simpledialog.askinteger(title='修改节点数目', prompt='请输入修改后的节点数目:', - initialvalue=str(len(d.Nodes))) - if node: - self.node_init(node) - G.draw() - - def node_del(self): - if d.nodes_num <= 2: - tk.messagebox.askokcancel(title='错误', message='节点过少') - return - nodes = len(d.Nodes) - 1 - if nodes < 6: - d.edgeinfo = 1 - self.node_init(nodes) - G.draw() - self.node_mark_display() - - def node_init(self, node): - if not node: - return - if d.source == 1: - d.data(node) - elif d.source == 2: - if node > 13: - tk.messagebox.askokcancel(title='错误', message='节点过多') - return - d.HEI_data(node) - else: - if node > 13: - tk.messagebox.askokcancel(title='错误', message='节点过多') - return - d.LIAO_data(node) - if node > 5: - d.edgeinfo = 0 - else: - d.edgeinfo = 1 - - def graph_del(self): - global d - del d - - - - def node_refresh(self): - nodes = len(d.Nodes) - self.node_init(nodes) - G.draw() - self.node_mark_display() - - def node_404(self, name): - # 弹出对话框 - tk.messagebox.askokcancel(title='错误', message='该节点不存在') - # print(result) - + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][ + result[j[0]][1]] = j[6] + distances[result[j[0]][1]][ + result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', + message='该图不存在dfs路径') + # print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], + ways[way + 1])][4] = True + self.end = time.time() # 记录结束时间 + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], + ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', + message='dfs算法路径') + self.end = time.time() # 记录结束时间 + def greedy(self, distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] \ + < min_distance and distances[curr_city][ + city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path if __name__ == '__main__': - G.draw() - node = Node() - node.node_mark_display() - mainloop() \ No newline at end of file + ···编程[11.2]与编程[11.5]主函数 + s. node_greedy () # 开始遍历搜索合法路径 \ No newline at end of file diff --git a/X4.py b/X4.py new file mode 100644 index 0000000..08e40ff --- /dev/null +++ b/X4.py @@ -0,0 +1,1464 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import messagebox +import numpy as np +import networkx as nx + + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] +CLICK = [0, 0, 0] + +class Data: + def __init__(self, source: int, num: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = (self.canvas_len // 2, self.canvas_len // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] + def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标 + result = [] + for i in range(n): + for j in range(i + 1, n): + result.append((i, j)) + return result + +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes) +# print(1) +root = Tk() # 设置主界面 +root.title("旅行商问题") # 设置标题 +root.geometry(f"{d.canvas_len + 700}x{d.canvas_len + 100}") # 设置大小 +root.resizable(0, 0) # 设置不能调整显示边框 + +frame = tk.Frame(root, padx=20, pady=20, width=d.canvas_len + 340, height=d.canvas_len + 50) +frame.grid() # 绘制显示框 + +frame_top = tk.Frame(frame, width=d.canvas_len, height=50) +frame_top.grid(row=0, column=0) # 绘制信息输出栏 + +cv = canvas = tk.Canvas(frame, bg='white', bd=2, relief="sunken", width=d.canvas_len + 330, height=d.canvas_len) +canvas.grid(row=1, column=0) # 放置绘图Canvas画布 + +frame_right = tk.Frame(root, bg=RED, width=300, height=d.canvas_len + 50) # 右边栏 +frame_right.grid(row=0, column=1) # 右边栏 + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + W.way_route_markings() + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + # print(ways) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + G.draw() + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self): + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.backtracking(distances) + print(self.way_sum) + if ways: # 判断路径是否存在 + self.ans = ways.copy() + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='回溯算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + else: + tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径') + G.draw() + return -1 + + def backtracking(self, distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + self.way_sum += 1 # 遍历路径的数目加一 + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + + def node_dfs(self): # 遍历 深度优先算法 + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + G.draw() + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + + def dfs_tsp(self,graph, start, current, path, visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.way_sum += 1 # 遍历路径的数目加一 + self.dfs_tsp(graph, start, next_node, path, visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + + def dfs(self,graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def find_hamiltonian_cycles(self,start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == np.inf: + return False + if node in path: + return False + return True + + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != np.inf: + cycles.append(path + [start]) + path.pop() + + find_paths(start) + return cycles + def check(self): + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 将路径中的 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # for i in distances: + # print(i) + all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances)) + + if all_hamiltonian_cycles: + tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways (): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + # global user_way + # global ans + s.node_dfs() + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return -1,0 + dis_user = 0 + dis_ans = 0 + # print(user_way) + # print(ans) + num = len(self.user_way) + # print(self.user_way) + # print(s.ans) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + # if dis_ans == dis_user: # 匹配成功,用户路径是最短路 + # return 1 + # # 匹配失败,不是最短路 + # return 0 + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + # print(s.ans) + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=80, height=30) + else: # 否则说明用户路径不是最短路径 + self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way = b_way[:-2] + lab2 = tk.Label(frame_right, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(self.user_way) == len(s.ans):# 路径长度不一样肯定不匹配,直接返回 + # self.auto_ways() + return 0 + way = [(int(node[1])-1) for node in d.Nodes] + result1 = list(map(str, self.user_way)) + result1 = ' '.join(result1) + ways = [way[an] for an in s.ans] + result2 = list(map(str, ways)) + result3 = ' '.join(result2) + result2.reverse() # 倒序排列列表元素 + result4 = ' '.join(result2) + if result4 == result1 or result3 == result1: + return -1 #-1表示是贪心路径 + return 1 + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + # 对用户路径进行标记与记录 + def way_route_markings(self): + global MARK_NODE + MARK_NODE = 2 + # E.newedge_del_all(1) + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + self.user_way = [0] + lab = tk.Label(frame_right, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + laba1.place(relx=0.00, rely=0.02 + y0, width=80, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_right, textvariable=default_value) + entry1.place(relx=0.3, rely=0.02 + y0, width=50, height=30) + labb1 = tk.Label(frame_right, text="当前下一节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + labb1.place(relx=0.48, rely=0.02 + y0, width=100, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_right) + entry2.place(relx=0.82, rely=0.02 + y0, width=50, height=30) + + labb3 = tk.Label(frame_right, text="是否进行连接:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + labb3.place(relx=0.2, rely=0.08 + y0, width=120, height=30) + # 按钮 + button = tk.Button(frame_right, text="确认", command=lambda: self.extract_values()) + button.place(relx=0.7, rely=0.08 + y0, width=80, height=30) + button2 = tk.Button(frame_right, text="撤销", command=lambda: self.revoke()) + button2.place(relx=0.01, rely=0.18 + y0, width=140, height=30) + button3 = tk.Button(frame_right, text="清除路径", command=lambda: self.clear_all_paths()) + button3.place(relx=0.52, rely=0.18 + y0, width=140, height=30) + button4 = tk.Button(frame_right, text="判断路径是否为贪心路径", command=lambda: self.greedy_path()) + button4.place(relx=0.01, rely=0.26 + y0, width=290, height=30) + button5 = tk.Button(frame_right, text="判断路径是否为最短路径", command=lambda: self.shortest_path()) + button5.place(relx=0.01, rely=0.33 + y0, width=290, height=30) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + global MARK_NODE + MARK_NODE = 2 + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + global MARK_NODE + MARK_NODE = 2 + no = G.way_node_edge(n1, n2) + flage = False + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + elif d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + else: + if self.user_way[-1] == int(d.Nodes[n2][1]) - 1: + nx = n1 + n1 = n2 + n2 = nx + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + flage = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + if flage: + G.draw() + self.user_path_display() + + def revoke(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + # print(g) + # print(self.user_way) + if g == -1: # 如果用户路径是贪心路径则显示 + lab1 = tk.Label(frame_right, text="用户路径是贪心路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=180, height=30) + elif g: + self.servey_greedy_path() + def servey_greedy_path(self): + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + lab2 = tk.Label(frame_right, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_right, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=180, height=30) + else: # 否则说明用户路径不是最短路径 + self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_right, text="用户路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.55, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_right, text=f"{way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.6, width=200, height=100) + + def way_operate(self): + global MARK_NODE + for i in range(len(d.Edges)): + d.Edges[i][4] = False + MARK_NODE = 0 + # E.newedge_del_all(1) + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + self.user_way = [0] + lab = tk.Label(frame_right, text="路径增加与删除", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + label1 = tk.Label(frame_right, text="节点1:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + label1.place(relx=0.00, rely=0.02 + y0, width=80, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_right) + entry3.place(relx=0.3, rely=0.02 + y0, width=50, height=30) + + label2 = tk.Label(frame_right, text="节点2:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + label2.place(relx=0.48, rely=0.02 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_right) + entry4.place(relx=0.82, rely=0.02 + y0, width=50, height=30) + + label3 = tk.Label(frame_right, text="是否进行两节点之间操作:", bg=RED, fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.1, rely=0.1 + y0, width=240, height=30) + + button = tk.Button(frame_right, text="线路增加", command=lambda: self.way_add()) + button.place(relx=0.25, rely=0.18 + y0, width=160, height=30) + button2 = tk.Button(frame_right, text="线路修改", command=lambda: self.way_change()) + button2.place(relx=0.25, rely=0.28 + y0, width=160, height=30) + button3 = tk.Button(frame_right, text="线路删除", command=lambda: self.way_delete()) + button3.place(relx=0.25, rely=0.38 + y0, width=160, height=30) + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self): + global MARK_NODE,Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + lab = tk.Label(frame_right, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + lab1 = tk.Label(frame_right, text=f"当前共有{len(d.Nodes)}个节点", bg=RED, fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.1, rely=0.0 + y0, width=200, height=30) + lab1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_right, text=text, bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.05 + y0, width=80, height=30) + lab2.place(relx=0.4, rely=0.05 + y0, width=80, height=30) + B = tk.Button(frame_right, text="删除当前节点", command=lambda: self.node_del_exact()) + B.place(relx=0.1, rely=0.1 + y0, width=150, height=30) + B = tk.Button(frame_right, text="增加一个节点", command=lambda: self.node_add()) + B.place(relx=0.1, rely=0.25 + y0, width=150, height=30) + B = tk.Button(frame_right, text="减少一个节点", command=lambda: self.node_del()) + B.place(relx=0.1, rely=0.3 + y0, width=150, height=30) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + +def refresh(): + node = random.randint(4, 10) + global d, MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node) + print(node) + W.user_way=[0] + G.draw() + W.way_route_markings() +def graph_init(): + global MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + G.draw() + W.way_route_markings() + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.node_dfs()) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.node_greedy()) + menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.tsp_backtracking()) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: # 限制节点操作 + N.node_mark_display() + elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作 + global CLICK + if CLICK[2] == 0: + CLICK[0] = n + elif CLICK[2] == 1: + CLICK[1] = n + W.extract_ligature(CLICK[0], CLICK[1]) + CLICK[2] = -1 + CLICK[2] += 1 + print(n) +def node_opera_mu(menu: tk.Menu): + """节点操作""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="添加一个节点", command=lambda: N.node_add()) + menu_node.add_command(label="删除一个节点", command=lambda: N.node_del()) + menu_node.add_command(label="选择一个节点", command=lambda: N.node_mark_display()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="节点操作", menu=menu_node) + +def path_opera_mu(menu: tk.Menu): + """路径操作""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="路径选择", command=lambda: W.way_route_markings()) + menu_node.add_command(label="路径修改", command=lambda: W.way_operate()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="路径操作", menu=menu_node, ) + +if __name__ == '__main__': + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + node_opera_mu(main_menu) + path_opera_mu(main_menu) + W.way_route_markings() + root.mainloop() diff --git a/X4_1.py b/X4_1.py new file mode 100644 index 0000000..9317b5a --- /dev/null +++ b/X4_1.py @@ -0,0 +1,1459 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/7 17:03 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4_1.py + +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import messagebox +import numpy as np + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] + +class Data: + def __init__(self, source: int, num: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = (self.canvas_len // 2, self.canvas_len // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes) +# print(1) +root = Tk() # 设置主界面 +root.title("旅行商问题") # 设置标题 +root.geometry(f"{d.canvas_len + 700}x{d.canvas_len + 100}") # 设置大小 +root.resizable(0, 0) # 设置不能调整显示边框 + +frame = tk.Frame(root, padx=20, pady=20, width=d.canvas_len + 340, height=d.canvas_len + 50) +frame.grid() # 绘制显示框 + +frame_top = tk.Frame(frame, width=d.canvas_len, height=50) +frame_top.grid(row=0, column=0) # 绘制信息输出栏 + +cv = canvas = tk.Canvas(frame, bg='white', bd=2, relief="sunken", width=d.canvas_len + 330, height=d.canvas_len) +canvas.grid(row=1, column=0) # 放置绘图Canvas画布 + +frame_right = tk.Frame(root, bg=RED, width=300, height=d.canvas_len + 50) # 右边栏 +frame_right.grid(row=0, column=1) # 右边栏 + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + W.way_route_markings() + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + ladder_sum = 0 + for j in range(1, l + 1): + ladder_sum += j - 1 + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + i += 1 + # print(j) + if i > ladder_sum: + break + # print(1) + else: + if j[6] < 99 and j[7] < 99: + distances[d.node_no(j[1])][d.node_no(j[2])] = j[6] + distances[d.node_no(j[2])][d.node_no(j[1])] = j[6] + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + # print(ways) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + G.draw() + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self,distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + def node_dfs(self): # 遍历 深度优先算法 + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + self.start = time.time() # 记录开始时间 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + ladder_sum = 0 + for j in range(1, l + 1): + ladder_sum += j - 1 + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + i += 1 + # print(j) + if i > ladder_sum: + break + # print(1) + else: + if j[6] < 99 and j[7] < 99: + distances[d.node_no(j[1])][d.node_no(j[2])] = j[6] + distances[d.node_no(j[2])][d.node_no(j[1])] = j[6] + cost, ways = self.dfs(distances) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + G.draw() + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + G.draw() + + def dfs_tsp(self,graph, start, current, path, visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.dfs_tsp(graph, start, next_node, path, visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + + def dfs(self,graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def graph_check(self, a, b): # 检查A点B点是否互通 + find = [] # 使用并查集来检测a,b点是否互通 + for i in range(len(d.Nodes)): # 初始化并查集 + find.append(i) # 使得find[i]=i + for edge in d.Edges: # 根据已有的边来更新并查集 + if (edge[8] == 1 and edge[3] == 0): + continue + x = edge[1] + y = edge[2] + while not x == find[x]: + x = find[x] + while not y == find[y]: + y = find[y] + if x < y: + find[y] = x + else: + find[x] = y + x = a + y = b + while not x == find[x]: # 查询a点所在集合 + x = find[x] + while not y == find[y]: # 查询b点所在集合 + y = find[y] + if find[x] == find[y]: # 输出检查结果 + messagebox.askokcancel(title='结果', message='节点 ' + str(a + 1) + '与节点 ' + str(b + 1) + '互通') + else: + messagebox.askokcancel(title='结果', message='节点 ' + str(a + 1) + '与节点 ' + str(b + 1) + '不互通') + + def check(self): # 检查是否存在最短路 + global MARK_NODE + MARK_NODE = 0 + find = [] + for i in range(len(d.Nodes)): + find.append(i) + for edge in d.Edges: + if edge[8] == 1 and edge[3] == 0: + continue + x = edge[1] + y = edge[2] + while not x == find[x]: + x = find[x] + while not y == find[y]: + y = find[y] + if x < y: + find[y] = x + else: + find[x] = y + flag = 1 + for i in range(len(d.Nodes)): + if not find[i] == find[0]: + flag = 0 + if flag: + tk.messagebox.askokcancel(title='结果', message='该图存在最短路') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways (): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + # global user_way + # global ans + s.node_dfs() + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return -1,0 + dis_user = 0 + dis_ans = 0 + # print(user_way) + # print(ans) + num = len(self.user_way) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i]+1, self.user_way[i+1] + 1)][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + # if dis_ans == dis_user: # 匹配成功,用户路径是最短路 + # return 1 + # # 匹配失败,不是最短路 + # return 0 + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=80, height=30) + else: # 否则说明用户路径不是最短路径 + self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + lab2 = tk.Label(frame_right, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(self.user_way) == len(s.ans):# 路径长度不一样肯定不匹配,直接返回 + self.auto_ways() + return + num = len(self.user_way) + for i in range(num):#逐个节点进行匹配 + if not self.user_way[i] == s.ans[i]:# 若第i位匹配失败则返回i + return i + return -1 #-1表示是贪心路径 + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + def show_GREDDY(self): + top = Toplevel(root, width = 500, height = 500) + top.title('判断路径是否为贪心路径')# 新建一个窗口来显示判断路径是否为贪心路径的结果 + lab1 = tk.Label(top, text="用户路径:", fg=BLACK, font=('微软雅黑', 20)) + u_way = ""# 显示用户路径 + for a in self.user_way: + u_way += d.Nodes[a][1] + u_way += "->" + u_way += d.Nodes[0][1] + # print(u_way) + y0 = 0.05 + lab2 = tk.Label(top, text=f"{u_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.10 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.05 + y0, width=200, height=100) + # 逐个节点比较用户路径是否为贪心路径 + g = self.check_user_greey() + print(g) + print(self.user_way) + if g == -1:#如果用户路径是贪心路径则显示 + result = '用户路径是贪心路径' + elif g: + result = '用户路径不是贪心路径' + result2 = '用户路径: ' + u_way + if len(s.ans) == 7: + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way += d.Nodes[0][1] + result2 += '\n贪心路径:' + g_way + else: + result2 += '\n贪心路径不存在' + lab = tk.Label(top, text=result2, bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab.place(relx=0.1, rely=0.5 + y0, width=250, height=100) + else:# 若用户路径不是贪心路径,则显示在第几步时选择出错,并将该次选择的正确选择与用户实际选择显示出来 + result = '用户路径不是贪心路径' + result2 = '用户路径:'+u_way + if len(s.ans) == 7: + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way += d.Nodes[0][1] + result2 += '\n贪心路径:' + g_way + else: + result2 += '\n贪心路径不存在' + lab = tk.Label(top, text=result2, bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab.place(relx=0.1, rely=0.5 + y0, width=250, height=100) + lab = tk.Label(top, text=result, bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=250, justify='left') + lab.place(relx=0.1, rely=0.3 + y0, width=200, height=50) + self.auto_ways()#将结果输出到输出框 + + # 对用户路径进行标记与记录 + def way_route_markings(self): + global MARK_NODE + MARK_NODE = 0 + # E.newedge_del_all(1) + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + self.user_way = [0] + lab = tk.Label(frame_right, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + laba1.place(relx=0.00, rely=0.02 + y0, width=80, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_right, textvariable=default_value) + entry1.place(relx=0.3, rely=0.02 + y0, width=50, height=30) + labb1 = tk.Label(frame_right, text="当前下一节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + labb1.place(relx=0.48, rely=0.02 + y0, width=100, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_right) + entry2.place(relx=0.82, rely=0.02 + y0, width=50, height=30) + + labb3 = tk.Label(frame_right, text="是否进行连接:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + labb3.place(relx=0.2, rely=0.08 + y0, width=120, height=30) + + button = tk.Button(frame_right, text="确认", command=lambda: self.extract_values()) + button.place(relx=0.7, rely=0.08 + y0, width=80, height=30) + button2 = tk.Button(frame_right, text="撤销", command=lambda: self.revoke()) + button2.place(relx=0.01, rely=0.18 + y0, width=140, height=30) + button3 = tk.Button(frame_right, text="清除路径", command=lambda: self.clear_all_paths()) + button3.place(relx=0.52, rely=0.18 + y0, width=140, height=30) + button4 = tk.Button(frame_right, text="判断路径是否为贪心路径", command=lambda: self.greedy_path()) + button4.place(relx=0.01, rely=0.26 + y0, width=290, height=30) + button5 = tk.Button(frame_right, text="判断路径是否为最短路径", command=lambda: self.shortest_path()) + button5.place(relx=0.01, rely=0.33 + y0, width=290, height=30) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + no = G.way_node_edge(n1, n2) + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + G.draw() + self.user_path_display() + def revoke(self): + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + # print(g) + # print(self.user_way) + if g == -1: # 如果用户路径是贪心路径则显示 + lab1 = tk.Label(frame_right, text="用户路径是贪心路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=180, height=30) + elif g: + self.servey_greedy_path() + def servey_greedy_path(self): + lab1 = tk.Label(frame_right, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way += d.Nodes[0][1] + lab2 = tk.Label(frame_right, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_right, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.8, width=180, height=30) + else: # 否则说明用户路径不是最短路径 + self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_right, text="用户路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.55, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_right, text=f"{way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.6, width=200, height=100) + + def way_operate(self): + global MARK_NODE + MARK_NODE = 0 + # E.newedge_del_all(1) + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + self.user_way = [0] + lab = tk.Label(frame_right, text="路径增加与删除", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + label1 = tk.Label(frame_right, text="节点1:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + label1.place(relx=0.00, rely=0.02 + y0, width=80, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_right) + entry3.place(relx=0.3, rely=0.02 + y0, width=50, height=30) + + label2 = tk.Label(frame_right, text="节点2:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + label2.place(relx=0.48, rely=0.02 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_right) + entry4.place(relx=0.82, rely=0.02 + y0, width=50, height=30) + + label3 = tk.Label(frame_right, text="是否进行两节点之间操作:", bg=RED, fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.1, rely=0.1 + y0, width=240, height=30) + + button = tk.Button(frame_right, text="线路增加", command=lambda: self.way_add()) + button.place(relx=0.25, rely=0.18 + y0, width=160, height=30) + button2 = tk.Button(frame_right, text="线路修改", command=lambda: self.way_change()) + button2.place(relx=0.25, rely=0.28 + y0, width=160, height=30) + button3 = tk.Button(frame_right, text="线路删除", command=lambda: self.way_delete()) + button3.place(relx=0.25, rely=0.38 + y0, width=160, height=30) + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self): + global MARK_NODE,Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_right.winfo_children(): + w.destroy() + # 清除右侧组件 + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + lab = tk.Label(frame_right, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + lab.place(relx=0.18, rely=0.02, width=200, height=30) + lab1 = tk.Label(frame_right, text=f"当前共有{len(d.Nodes)}个节点", bg=RED, fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.1, rely=0.0 + y0, width=200, height=30) + lab1 = tk.Label(frame_right, text="当前节点:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_right, text=text, bg=BLUE, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.05 + y0, width=80, height=30) + lab2.place(relx=0.4, rely=0.05 + y0, width=80, height=30) + B = tk.Button(frame_right, text="删除当前节点", command=lambda: self.node_del_exact()) + B.place(relx=0.1, rely=0.1 + y0, width=150, height=30) + B = tk.Button(frame_right, text="增加一个节点", command=lambda: self.node_add()) + B.place(relx=0.1, rely=0.25 + y0, width=150, height=30) + B = tk.Button(frame_right, text="减少一个节点", command=lambda: self.node_del()) + B.place(relx=0.1, rely=0.3 + y0, width=150, height=30) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display() + + +def refresh(): + node = random.randint(4, 10) + global d, MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node) + print(node) + W.user_way=[0] + G.draw() + W.way_route_markings() +def graph_init(): + global MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + G.draw() + W.way_route_markings() + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.node_dfs()) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.node_greedy()) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: + N.node_mark_display() + elif MARK_NODE == 2: + pass + print(n) +def node_opera_mu(menu: tk.Menu): + """节点操作""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="添加一个节点", command=lambda: N.node_add()) + menu_node.add_command(label="删除一个节点", command=lambda: N.node_del()) + menu_node.add_command(label="选择一个节点", command=lambda: N.node_mark_display()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="节点操作", menu=menu_node) + +def path_opera_mu(menu: tk.Menu): + """路径操作""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="路径选择", command=lambda: W.way_route_markings()) + menu_node.add_command(label="路径修改", command=lambda: W.way_operate()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="路径操作", menu=menu_node, ) + +if __name__ == '__main__': + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + node_opera_mu(main_menu) + path_opera_mu(main_menu) + W.way_route_markings() + root.mainloop() diff --git a/X4_2.py b/X4_2.py new file mode 100644 index 0000000..0a35fed --- /dev/null +++ b/X4_2.py @@ -0,0 +1,1615 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import ttk +from tkinter import messagebox +import numpy as np +import networkx as nx +from PIL import Image, ImageTk + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] +CLICK = [0, 0, 0] + +List_Image = [] + +class Data: + def __init__(self, source: int, num: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = ((self.canvas_len+220) // 2, (self.canvas_len+230) // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] + def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标 + result = [] + for i in range(n): + for j in range(i + 1, n): + result.append((i, j)) + return result + +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes) + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, bg='white', font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + window(2) + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + print(self.way_sum) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.ans.clear() + self.end = time.time() # 记录结束时间 + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self): + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.backtracking(distances) + print(self.way_sum) + + if ways: # 判断路径是否存在 + self.ans = ways.copy() + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='回溯算法路径') + self.end = time.time() # 记录结束时间 + else: + tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径') + self.end = time.time() # 记录结束时间 + return -1 + + def backtracking(self, distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + self.way_sum += 1 # 遍历路径的数目加一 + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + + def auto_solve(self, number): + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + if number == 1: # 回溯 + self.tsp_backtracking() + elif number == 2: + self.node_greedy() # 贪心 + else: # 深度优先算法 + self.node_dfs() + window(1) + G.draw() + + def node_dfs(self): # 遍历 深度优先算法 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + # print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.end = time.time() # 记录结束时间 + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + + + def dfs_tsp(self,graph, start, current, path, + visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[ + current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and \ + not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.way_sum += 1 # 遍历路径的数目加一 + self.dfs_tsp(graph, start, next_node, path, + visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + def dfs(self, graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, + visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def find_hamiltonian_cycles(self,start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == np.inf: + return False + if node in path: + return False + return True + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != np.inf: + cycles.append(path + [start]) + path.pop() + + find_paths(start) + return cycles + def check(self): + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 将路径中的 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # for i in distances: + # print(i) + all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances)) + + if all_hamiltonian_cycles: + tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways (): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + # global user_way + # global ans + s.node_dfs() + if not len(s.ans): + return -1, 0 + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return 0, 0 + dis_user = 0 + dis_ans = 0 + # print(user_way) + # print(ans) + num = len(self.user_way) + # print(self.user_way) + # print(s.ans) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + # if dis_ans == dis_user: # 匹配成功,用户路径是最短路 + # return 1 + # # 匹配失败,不是最短路 + # return 0 + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + # print(s.ans) + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way = b_way[:-2] + lab2 = tk.Label(frame_left, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(s.ans): + return -2 + # 路径长度不一样肯定不匹配,直接返回 + if not len(self.user_way) == len(s.ans): + # self.auto_ways() + return 0 + way = [(int(node[1])-1) for node in d.Nodes] + result1 = list(map(str, self.user_way)) + result1 = ' '.join(result1) + ways = [way[an] for an in s.ans] + result2 = list(map(str, ways)) + result3 = ' '.join(result2) + result2.reverse() # 倒序排列列表元素 + result4 = ' '.join(result2) + if result4 == result1 or result3 == result1: + return -1 #-1表示是贪心路径 + return 1 + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + # 对用户路径进行标记与记录 + def way_route_markings(self, event): + global MARK_NODE + MARK_NODE = 2 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + + left_window() + self.user_way = [0] + # lab = tk.Label(frame_left, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + laba1.place(relx=0.2, rely=0.28 + y0, width=140, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_left, textvariable=default_value, bg="#F5F5F5") + entry1.place(relx=0.6, rely=0.28 + y0, width=50, height=30) + labb1 = tk.Label(frame_left, text="当前下一节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb1.place(relx=0.2, rely=0.33 + y0, width=140, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_left, bg="#F5F5F5") + entry2.place(relx=0.6, rely=0.33 + y0, width=50, height=30) + + labb3 = tk.Label(frame_left, text="是否进行连接:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb3.place(relx=0.2, rely=0.4 + y0, width=140, height=30) + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[38], bg='white', command=lambda: self.extract_values()) # 确定 + button.place(relx=0.55, rely=0.38 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[36], bg='white', command=lambda: self.revoke()) # 撤销 + button2.place(relx=0.18, rely=0.46 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[37], bg='white', command=lambda: self.clear_all_paths()) # 清除路径 + button3.place(relx=0.5, rely=0.46 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[42], bg='white', command=lambda: self.greedy_path()) # 判断路径是否为贪心路径 + button4.place(relx=0.18, rely=0.55 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[43], bg='white', command=lambda: self.shortest_path()) # 判断路径是否为最短路径 + button5.place(relx=0.18, rely=0.62 + y0) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + global MARK_NODE + MARK_NODE = 2 + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + global MARK_NODE + MARK_NODE = 2 + no = G.way_node_edge(n1, n2) + flage = False + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + elif d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + else: + if self.user_way[-1] == int(d.Nodes[n2][1]) - 1: + nx = n1 + n1 = n2 + n2 = nx + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + flage = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + if flage: + G.draw() + self.user_path_display() + + def revoke(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + if g == -2: + # cvx.create_text(0, 0, text="不存在贪心路径") + lab1 = tk.Label(frame_left, text="不存在贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + elif g == -1: # 如果用户路径是贪心路径则显示 + # cvx.create_text(0, 0, text="用户路径是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + # elif g: + # self.servey_greedy_path() + else: + # cvx.create_text(0, 0, text="用户路径不是是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径不是是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + def servey_greedy_path(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + lab2 = tk.Label(frame_left, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + # if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + # lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + # lab1.place(relx=0.1, rely=0.8, width=180, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_left, text="用户路径:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.15, rely=0.8, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_left, text=f"{way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.18, rely=0.84, width=300, height=100) + + def way_operate(self, event): + global MARK_NODE + for i in range(len(d.Edges)): + d.Edges[i][4] = False + MARK_NODE = 5 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + self.user_way = [0] + # lab = tk.Label(frame_left, text="路径增加与删除", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + label1 = tk.Label(frame_left, text="节点1:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label1.place(relx=0.15, rely=0.32 + y0, width=100, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_left, bg='#F5F5F5') + entry3.place(relx=0.6, rely=0.32 + y0, width=50, height=30) + + label2 = tk.Label(frame_left, text="节点2:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label2.place(relx=0.15, rely=0.42 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_left, bg='#F5F5F5') + entry4.place(relx=0.6, rely=0.42 + y0, width=50, height=30) + + label3 = tk.Label(frame_left, text="是否进行两节点之间操作:", bg='white', fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.15, rely=0.5 + y0, width=240, height=30) + + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[33], bg='white', + command=lambda: self.way_add()) # 线路增加 + button.place(relx=0.2, rely=0.56 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[34], bg='white', + command=lambda: self.way_change()) # 线路修改 + button2.place(relx=0.2, rely=0.66 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[35], bg='white', + command=lambda: self.way_delete()) # 线路删除 + button3.place(relx=0.2, rely=0.76 + y0) + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self, event): + global MARK_NODE, Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + # lab = tk.Label(frame_left, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.32, width=200, height=30) + labx1 = tk.Label(frame_left, text=f"当前共有{len(d.Nodes)}个节点", bg='white', fg=BLACK, font=('微软雅黑', 20)) + labx1.place(relx=0.15, rely=0.3 + y0, width=260, height=30) + lab1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 20)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_left, text=text, bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 20)) + lab1.place(relx=0.15, rely=0.4 + y0, width=160, height=50) + lab2.place(relx=0.6, rely=0.4 + y0, width=100, height=50) + + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[30], bg='white', + command=lambda: self.node_del_exact()) # 删除当前节点 + button.place(relx=0.2, rely=0.5 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[32], bg='white', + command=lambda: self.node_add()) # 增加一个节点 + button2.place(relx=0.2, rely=0.6 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[31], bg='white', + command=lambda: self.node_del()) # 减少一个节点 + button3.place(relx=0.2, rely=0.7 + y0) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + +def refresh(): + node = random.randint(4, 10) + global d, MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node) + print(node) + W.user_way=[0] + G.draw() + window(2) + +def graph_init(): + global MODE,MARK_NODE + MARK_NODE = 0 + MODE = 0 + G.draw() + window(2) + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.auto_solve(3)) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.auto_solve(2)) + menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.auto_solve(1)) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + global entry1, entry2, default_value + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: # 限制节点操作 + N.node_mark_display(1) + elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作 + global CLICK + print(14) + if CLICK[2] == 0: + CLICK[0] = n + default_value.set(d.Nodes[n][1]) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + print(15) + elif CLICK[2] == 1: + CLICK[1] = n + W.extract_ligature(CLICK[0], CLICK[1]) + CLICK[2] = -1 + CLICK[2] += 1 + print(n) + +def element_reading(): + global List_Image + folder_path = 'K:/work/traveler/background/' + image_path = ['1_红.png', '1_绿.png', '1_蓝.png', '2_红.png', '2_绿.png', '2_蓝.png', '3_红.png', '3_绿.png', '3_蓝.png', + '4_红.png', '4_绿.png', '4_蓝.png', '5_红.png', '5_绿.png', '5_蓝.png', '6_红.png', '6_绿.png', '6_蓝.png', + '7_红.png', '7_绿.png', '7_蓝.png', '8_红.png', '8_绿.png', '8_蓝.png', '9_红.png', '9_绿.png', '9_蓝.png', + '10_红.png', '10_绿.png', '10_蓝.png', '删除节点.png', '减少节点.png', '增加节点.png', '线路增加.png', + '线路修改.png', '线路删除.png', '撤销.png', '清除路径.png', '确认.png', '编组9.png', '背景.png', + '矩形.png', '判断路径是否为贪心路径.png', '判断路径是否为最短路径.png', '矩形2.png'] + for path in image_path[:30]: # 1-10球加载 + List_Image.append(element(folder_path+path, 50, 50)) + for path in image_path[30:36]: # 节点操作与连线操作功能按钮 + List_Image.append(element(folder_path + path, 280, 50)) + List_Image.append(element(folder_path + image_path[36], 140, 50)) # 撤销 + List_Image.append(element(folder_path + image_path[37], 140, 50)) # 清除路径 + List_Image.append(element(folder_path + image_path[38], 120, 50)) # 确认 + List_Image.append(element(folder_path + image_path[39], 380, 100)) # 编组_9 + List_Image.append(element(folder_path + image_path[40], 900, 900)) # 背景 + List_Image.append(element(folder_path + image_path[41], 380, 140)) # 小矩形 + List_Image.append(element(folder_path + image_path[42], 300, 50)) # 判断路径是否为贪心路径 + List_Image.append(element(folder_path + image_path[43], 300, 50)) # 判断路径是否为最短路径 + List_Image.append(element(folder_path + image_path[44], 380, 530)) # 大矩形 + +def element(path, width, height): + # 加载图元对应的图片文件 + img = Image.open(path) + # 使用resize方法调整图片 + img = img.resize((width, height)) + # 把Image对象转换成PhotoImage对象 + img = ImageTk.PhotoImage(img) + # 保存图片的引用,防止被垃圾回收 + root.img = img + return img + +def window(event): + global MARK_NODE + MARK_NODE = 0 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + Label_1 = tk.Label(frame_left, text="遍历节点数目", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_1.place(x=50, y=330, width=180, height=40) + Label_2 = tk.Label(frame_left, text="求解花费时间", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_2.place(x=50, y=450, width=180, height=40) + Label_3 = tk.Label(frame_left, text="最短路径", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_3.place(x=50, y=570, width=140, height=40) + if event == 2: + lab1 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + lab2 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + lab3 = tk.Label(frame_left, text=f"{1}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + else: + lab1 = tk.Label(frame_left, text=f"{s.way_sum}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + t = round(s.end - s.start, 4) + lab2 = tk.Label(frame_left, text=f"{t}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + g_way = '' + if len(s.ans) > 0: + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + else: + g_way='1' + lab3 = tk.Label(frame_left, text=f"{g_way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + +def left_window(): + background_button = tk.Label(frame_left, image=List_Image[39], bg='#FFFAF0') # 编组 + background_button.place(x=50, y=20, width=380, height=100) + background_button.bind("", window) + # # 创建 Label 组件来显示背景图片 + background_on = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[41]) # 小矩形背景 + background_on.place(x=50, y=140, width=380, height=140) + + if MARK_NODE == 1: + background_color = [BLACK, '#DCDCDC', '#DCDCDC'] + elif MARK_NODE == 5: + background_color = ['#DCDCDC', BLACK, '#DCDCDC'] + elif MARK_NODE == 2: + background_color = ['#DCDCDC', '#DCDCDC', BLACK] + else: + background_color = ['#DCDCDC', '#DCDCDC', '#DCDCDC'] + Label_button1 = tk.Label(frame_left, text="节点操作", bg='white', fg=background_color[0], font=('微软雅黑', 15)) + Label_button1.place(x=60, y=180, width=80, height=50) + Label_button1.bind("", N.node_mark_display) + Label_button2 = tk.Label(frame_left, text="连接操作", bg='white', fg=background_color[1], font=('微软雅黑', 15)) + Label_button2.place(x=200, y=180, width=80, height=50) + Label_button2.bind("", W.way_operate) + Label_button3 = tk.Label(frame_left, text="路径操作", bg='white', fg=background_color[2], font=('微软雅黑', 15)) + Label_button3.place(x=340, y=180, width=80, height=50) + Label_button3.bind("", W.way_route_markings) + + background_below = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[44]) # 大矩形背景 + background_below.place(x=50, y=300, width=380, height=530) + +if __name__ == '__main__': + root = Tk() # 设置主界面 + root.title("旅行商问题") # 设置标题 + root.geometry("1400x900") # 设置大小 + + root.resizable(0, 0) # 设置不能调整显示边框 + element_reading() + frame_top = tk.Frame(root, bg='white', width=1400, height=50, highlightthickness=0) + frame_top.place(x=0, y=0) # 绘制信息输出栏 + + frame_left = tk.Frame(root, bg='#FFFAF0', highlightthickness=0, width=500, height=850) # 左边栏 + frame_left.place(x=0, y=50) # 左边栏 + + cv = tk.Canvas(root, bg='#FFFAF0', bd=0, relief="flat", width=900, height=850, highlightthickness=0) + cv.place(x=500, y=50) # 放置绘图Canvas画布 + + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) # 背景 + + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + window(2) + G.lab_show(frame_top, 0, 0, 0) # 显示边信息 + root.mainloop() diff --git a/X4_3.py b/X4_3.py new file mode 100644 index 0000000..e73b935 --- /dev/null +++ b/X4_3.py @@ -0,0 +1,1730 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import ttk +from tkinter import messagebox +import numpy as np +import networkx as nx +from PIL import Image, ImageTk + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] +CLICK = [0, 0, 0] + +List_Image = [] + +class Data: + def __init__(self, source: int, num: int, mold: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.styple = mold + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = ((self.canvas_len+220) // 2, (self.canvas_len+230) // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.styple == 1: + dist_c = random.randint(1, 99) # 随机生成距离成本 + time_c = random.randint(1, 99) # 随机生成链接的时间 + else: + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] + def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标 + result = [] + for i in range(n): + for j in range(i + 1, n): + result.append((i, j)) + return result + +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes, 0) + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, bg='white', font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + window(2) + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + print(self.way_sum) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.ans.clear() + self.end = time.time() # 记录结束时间 + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self): + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.backtracking(distances) + print(self.way_sum) + + if ways: # 判断路径是否存在 + self.ans = ways.copy() + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='回溯算法路径') + self.end = time.time() # 记录结束时间 + else: + tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径') + self.end = time.time() # 记录结束时间 + return -1 + + def backtracking(self, distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + self.way_sum += 1 # 遍历路径的数目加一 + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + + def auto_solve(self, number): + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + if number == 1: # 回溯 + self.tsp_backtracking() + elif number == 2: + self.node_greedy() # 贪心 + else: # 深度优先算法 + self.node_dfs() + window(1) + G.draw() + + def node_dfs(self): # 遍历 深度优先算法 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + # print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.end = time.time() # 记录结束时间 + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + + + def dfs_tsp(self,graph, start, current, path, + visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[ + current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and \ + not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.way_sum += 1 # 遍历路径的数目加一 + self.dfs_tsp(graph, start, next_node, path, + visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + def dfs(self, graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, + visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def find_hamiltonian_cycles(self,start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == np.inf: + return False + if node in path: + return False + return True + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != np.inf: + cycles.append(path + [start]) + path.pop() + find_paths(start) + return cycles + def check(self): + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 将路径中的 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances)) + l = len(all_hamiltonian_cycles) + if l: + for cycles in all_hamiltonian_cycles: + dis_ans = 0 + for i in range(d.nodes_num): + dis_ans += d.Edges[G.way_node_edge(cycles[i], cycles[i + 1])][6] + print(cycles,dis_ans) + if all_hamiltonian_cycles: + tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways(): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + s.node_dfs() + if not len(s.ans): + return -1, 0 + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return 0, 0 + dis_user = 0 + dis_ans = 0 + num = len(self.user_way) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + # print(s.ans) + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way = b_way[:-2] + lab2 = tk.Label(frame_left, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(s.ans): + return -2 + # 路径长度不一样肯定不匹配,直接返回 + if not len(self.user_way) == len(s.ans): + # self.auto_ways() + return 0 + way = [(int(node[1])-1) for node in d.Nodes] + result1 = list(map(str, self.user_way)) + result1 = ' '.join(result1) + ways = [way[an] for an in s.ans] + result2 = list(map(str, ways)) + result3 = ' '.join(result2) + result2.reverse() # 倒序排列列表元素 + result4 = ' '.join(result2) + if result4 == result1 or result3 == result1: + return -1 #-1表示是贪心路径 + else: + x_1 = self.find_longest(result1, result4) + x_2 = self.find_longest(result1, result3) + return max(x_1, x_2) + + def find_longest(self, list1, list2): + index = 0 + while index < len(list1) and index < len(list2): + if list1[index] != list2[index]: + break + index += 1 + return index + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + # 对用户路径进行标记与记录 + def way_route_markings(self, event): + global MARK_NODE + MARK_NODE = 2 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + + left_window() + self.user_way = [0] + # lab = tk.Label(frame_left, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + laba1.place(relx=0.2, rely=0.28 + y0, width=140, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_left, textvariable=default_value, bg="#F5F5F5") + entry1.place(relx=0.6, rely=0.28 + y0, width=50, height=30) + labb1 = tk.Label(frame_left, text="当前下一节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb1.place(relx=0.2, rely=0.33 + y0, width=140, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_left, bg="#F5F5F5") + entry2.place(relx=0.6, rely=0.33 + y0, width=50, height=30) + + labb3 = tk.Label(frame_left, text="是否进行连接:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb3.place(relx=0.2, rely=0.4 + y0, width=140, height=30) + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[38], bg='white', command=lambda: self.extract_values()) # 确定 + button.place(relx=0.55, rely=0.38 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[36], bg='white', command=lambda: self.revoke()) # 撤销 + button2.place(relx=0.18, rely=0.46 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[37], bg='white', command=lambda: self.clear_all_paths()) # 清除路径 + button3.place(relx=0.5, rely=0.46 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[42], bg='white', command=lambda: self.greedy_path()) # 判断路径是否为贪心路径 + button4.place(relx=0.18, rely=0.55 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[43], bg='white', command=lambda: self.shortest_path()) # 判断路径是否为最短路径 + button5.place(relx=0.18, rely=0.62 + y0) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + global MARK_NODE + MARK_NODE = 2 + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + global MARK_NODE + MARK_NODE = 2 + no = G.way_node_edge(n1, n2) + flage = False + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + elif d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + else: + if self.user_way[-1] == int(d.Nodes[n2][1]) - 1: + nx = n1 + n1 = n2 + n2 = nx + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + flage = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + if flage: + G.draw() + self.user_path_display() + + def revoke(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + if g == -2: + # cvx.create_text(0, 0, text="不存在贪心路径") + lab1 = tk.Label(frame_left, text="不存在贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + elif g == -1: # 如果用户路径是贪心路径则显示 + # cvx.create_text(0, 0, text="用户路径是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + # elif g: + # self.servey_greedy_path() + else: + # print(self.user_way) + # print(self.user_way[0:g]) + # cvx.create_text(0, 0, text="用户路径不是是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径不是是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + + def servey_greedy_path(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + lab2 = tk.Label(frame_left, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + # if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + # lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + # lab1.place(relx=0.1, rely=0.8, width=180, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_left, text="用户路径:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.15, rely=0.8, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_left, text=f"{way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.18, rely=0.84, width=300, height=100) + + def way_operate(self, event): + global MARK_NODE + for i in range(len(d.Edges)): + d.Edges[i][4] = False + MARK_NODE = 5 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除左侧组件 + left_window() + self.user_way = [0] + y0 = 0.1 + label1 = tk.Label(frame_left, text="节点1:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label1.place(relx=0.15, rely=0.32 + y0, width=100, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_left, bg='#F5F5F5') + entry3.place(relx=0.6, rely=0.32 + y0, width=50, height=30) + + label2 = tk.Label(frame_left, text="节点2:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label2.place(relx=0.15, rely=0.37 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_left, bg='#F5F5F5') + entry4.place(relx=0.6, rely=0.37 + y0, width=50, height=30) + button1 = tk.Button(frame_left, text='完全图', relief="flat", bg='#00BFFF', fg=BLACK, + font=('微软雅黑', 15), command=lambda: self.complete_graph()) # 完全图 + button1.place(relx=0.2, rely=0.43 + y0, width=100, height=30) + button2 = tk.Button(frame_left, text='可达判断', relief="flat", bg='#00BFFF', fg=BLACK, + font=('微软雅黑', 15), command=lambda: self.reachable_judgment()) # 节点可达性判断 + button2.place(relx=0.6, rely=0.43 + y0, width=100, height=30) + + + label3 = tk.Label(frame_left, text="是否进行两节点之间操作:", bg='white', fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.15, rely=0.5 + y0, width=240, height=30) + # 按钮 + button3 = tk.Button(frame_left, relief="flat", image=List_Image[33], bg='white', + command=lambda: self.way_add()) # 线路增加 + button3.place(relx=0.2, rely=0.56 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[34], bg='white', + command=lambda: self.way_change()) # 线路修改 + button4.place(relx=0.2, rely=0.66 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[35], bg='white', + command=lambda: self.way_delete()) # 线路删除 + button5.place(relx=0.2, rely=0.76 + y0) + + def is_reachable(self, graph, start, target, visited): + if start == target: + return True + visited.add(start) + for neighbor in graph[start]: + if neighbor not in visited: + if self.is_reachable(graph, neighbor, target, visited): + return True + return False + + def complete_graph(self): + l = len(d.Nodes) + result = d.node_coordinate(l) + for index in range(len(result)): + if d.Edges[index][3] == 0: + dist_c = random.randint(10, 99) # 随机生成距离成本 + time_c = random.randint(10, 99) # 随机生成链接的时间 + d.Edges[index][3] = 1 + d.Edges[index][6] = dist_c + d.Edges[index][7] = time_c + G.draw() + + + def reachable_judgment(self): + try: + a = int(entry3.get()) # 获取第一个输入框的值 + b = int(entry4.get()) # 获取第二个输入框的值 + l = len(d.Nodes) + index_list = [] + for i in d.Nodes: + index_list.append(i[1]) + dictionary = {i: [] for i in range(1, l + 1)} + result = d.node_coordinate(l) + # 生成无向图 + for i in range(len(result)): + if d.Edges[i][3] == 1: + x = d.Edges[i][5].split('-')[0] + y = d.Edges[i][5].split('-')[-1] + dictionary[index_list.index(x)+1].append(index_list.index(y)+1) + dictionary[index_list.index(y)+1].append(index_list.index(x)+1) + start_node = index_list.index(str(a))+1 + target_node = index_list.index(str(b))+1 + visited_nodes = set() + print(dictionary) + flag = self.is_reachable(dictionary, start_node, target_node, visited_nodes) + if flag: + tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}可达') + else: + tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}不可达') + except ValueError: + tk.messagebox.askokcancel(title='结果', message='请输入节点') + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self, event): + global MARK_NODE, Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + # lab = tk.Label(frame_left, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.32, width=200, height=30) + labx1 = tk.Label(frame_left, text=f"当前共有{len(d.Nodes)}个节点", bg='white', fg=BLACK, font=('微软雅黑', 20)) + labx1.place(relx=0.15, rely=0.3 + y0, width=260, height=30) + lab1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 20)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_left, text=text, bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 20)) + lab1.place(relx=0.15, rely=0.4 + y0, width=160, height=50) + lab2.place(relx=0.6, rely=0.4 + y0, width=100, height=50) + + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[30], bg='white', + command=lambda: self.node_del_exact()) # 删除当前节点 + button.place(relx=0.2, rely=0.5 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[32], bg='white', + command=lambda: self.node_add()) # 增加一个节点 + button2.place(relx=0.2, rely=0.6 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[31], bg='white', + command=lambda: self.node_del()) # 减少一个节点 + button3.place(relx=0.2, rely=0.7 + y0) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + +def refresh(): + top = creat_window('路径生成') # 创建弹出窗口 + distance_entry = create_input_box(top, "节点个数:(1-10)", 6) # 创建一个输入框,获取节点个数 + # 创建一个下拉框,用于选择生成图类型 + mode_combobox = create_dropdown_box(top, "生成图类型", ["TSP", "完全图"]) # 创建一个下拉框,用于选择生成图类型 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + # 从下拉框中获取池化类型 + result2 = mode_combobox.get() + print(result1, result2) + flag = 0 + if result2!=None: + if result2 == '完全图': + flag = 1 + if 10 > result1 > 0: + global d + if flag: + d = Data(0, result1, 1) + else: + d = Data(0, result1, 0) + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='节点数范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的节点数') + + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + +def graph_init(): + node = random.randint(4, 10) + global d, MODE, MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node,0) + print(node) + W.user_way = [0] + G.draw() + window(2) + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.auto_solve(3)) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.auto_solve(2)) + menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.auto_solve(1)) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +# 下拉框 +def create_dropdown_box(top, text, listvalues): + # 创建一个下拉框,用于选择 + box_mode_label = tk.Label(top, text=text) + box_mode_label.pack(padx=10, pady=10) + box_mode_combobox = ttk.Combobox(top) + box_mode_combobox["values"] = listvalues + box_mode_combobox.pack(padx=20, pady=15) + return box_mode_combobox + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + global entry1, entry2, default_value, CLICK, entry3, entry4 + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: # 限制节点操作 + N.node_mark_display(1) + elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作 + if CLICK[2] == 0: + CLICK[0] = n + default_value.set(d.Nodes[n][1]) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + elif CLICK[2] == 1: + CLICK[1] = n + W.extract_ligature(CLICK[0], CLICK[1]) + CLICK[2] = -1 + CLICK[2] += 1 + elif MARK_NODE == 5: # 若是MARK_NODE==5,则可以进行节点连接操作 + if CLICK[2] == 0: + CLICK[0] = n + entry3.delete(0, tk.END) + # 设置输入框的值为 节点 + entry3.insert(0, d.Nodes[n][1]) + elif CLICK[2] == 1: + CLICK[1] = n + # W.extract_ligature(CLICK[0], CLICK[1]) + entry4.delete(0, tk.END) + # 设置输入框的值为 节点 + entry4.insert(0, d.Nodes[n][1]) + CLICK[2] = -1 + CLICK[2] += 1 + print(n) + +def element_reading(): + global List_Image + folder_path = 'K:/work/traveler/background/' + image_path = ['1_红.png', '1_绿.png', '1_蓝.png', '2_红.png', '2_绿.png', '2_蓝.png', '3_红.png', '3_绿.png', '3_蓝.png', + '4_红.png', '4_绿.png', '4_蓝.png', '5_红.png', '5_绿.png', '5_蓝.png', '6_红.png', '6_绿.png', '6_蓝.png', + '7_红.png', '7_绿.png', '7_蓝.png', '8_红.png', '8_绿.png', '8_蓝.png', '9_红.png', '9_绿.png', '9_蓝.png', + '10_红.png', '10_绿.png', '10_蓝.png', '删除节点.png', '减少节点.png', '增加节点.png', '线路增加.png', + '线路修改.png', '线路删除.png', '撤销.png', '清除路径.png', '确认.png', '编组9.png', '背景.png', + '矩形.png', '判断路径是否为贪心路径.png', '判断路径是否为最短路径.png', '矩形2.png', '完全图.png', '可达判断.png'] + for path in image_path[:30]: # 1-10球加载 + List_Image.append(element(folder_path+path, 50, 50)) + for path in image_path[30:36]: # 节点操作与连线操作功能按钮 + List_Image.append(element(folder_path + path, 280, 50)) + List_Image.append(element(folder_path + image_path[36], 140, 50)) # 撤销 + List_Image.append(element(folder_path + image_path[37], 140, 50)) # 清除路径 + List_Image.append(element(folder_path + image_path[38], 120, 50)) # 确认 + List_Image.append(element(folder_path + image_path[39], 380, 100)) # 编组_9 + List_Image.append(element(folder_path + image_path[40], 900, 900)) # 背景 + List_Image.append(element(folder_path + image_path[41], 380, 140)) # 小矩形 + List_Image.append(element(folder_path + image_path[42], 300, 50)) # 判断路径是否为贪心路径 + List_Image.append(element(folder_path + image_path[43], 300, 50)) # 判断路径是否为最短路径 + List_Image.append(element(folder_path + image_path[44], 380, 530)) # 大矩形 + List_Image.append(element(folder_path + image_path[45], 140, 50)) # 完全图 + List_Image.append(element(folder_path + image_path[46], 140, 50)) # 可达判断 + +def element(path, width, height): + # 加载图元对应的图片文件 + img = Image.open(path) + # 使用resize方法调整图片 + img = img.resize((width, height)) + # 把Image对象转换成PhotoImage对象 + img = ImageTk.PhotoImage(img) + # 保存图片的引用,防止被垃圾回收 + root.img = img + return img + +def window(event): + global MARK_NODE + MARK_NODE = 0 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + Label_1 = tk.Label(frame_left, text="遍历节点数目", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_1.place(x=50, y=330, width=180, height=40) + Label_2 = tk.Label(frame_left, text="求解花费时间", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_2.place(x=50, y=450, width=180, height=40) + Label_3 = tk.Label(frame_left, text="最短路径", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_3.place(x=50, y=570, width=140, height=40) + if event == 2: + lab1 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + lab2 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + lab3 = tk.Label(frame_left, text=f"{1}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + else: + lab1 = tk.Label(frame_left, text=f"{s.way_sum}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + t = round(s.end - s.start, 4) + lab2 = tk.Label(frame_left, text=f"{t}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + g_way = '' + if len(s.ans) > 0: + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + else: + g_way='1' + lab3 = tk.Label(frame_left, text=f"{g_way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + +def left_window(): + background_button = tk.Label(frame_left, image=List_Image[39], bg='#FFFAF0') # 编组 + background_button.place(x=50, y=20, width=380, height=100) + background_button.bind("", window) + # # 创建 Label 组件来显示背景图片 + background_on = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[41]) # 小矩形背景 + background_on.place(x=50, y=140, width=380, height=140) + + if MARK_NODE == 1: + background_color = [BLACK, '#DCDCDC', '#DCDCDC'] + elif MARK_NODE == 5: + background_color = ['#DCDCDC', BLACK, '#DCDCDC'] + elif MARK_NODE == 2: + background_color = ['#DCDCDC', '#DCDCDC', BLACK] + else: + background_color = ['#DCDCDC', '#DCDCDC', '#DCDCDC'] + Label_button1 = tk.Label(frame_left, text="节点操作", bg='white', fg=background_color[0], font=('微软雅黑', 15)) + Label_button1.place(x=60, y=180, width=80, height=50) + Label_button1.bind("", N.node_mark_display) + Label_button2 = tk.Label(frame_left, text="连接操作", bg='white', fg=background_color[1], font=('微软雅黑', 15)) + Label_button2.place(x=200, y=180, width=80, height=50) + Label_button2.bind("", W.way_operate) + Label_button3 = tk.Label(frame_left, text="路径操作", bg='white', fg=background_color[2], font=('微软雅黑', 15)) + Label_button3.place(x=340, y=180, width=80, height=50) + Label_button3.bind("", W.way_route_markings) + + background_below = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[44]) # 大矩形背景 + background_below.place(x=50, y=300, width=380, height=530) + +if __name__ == '__main__': + root = Tk() # 设置主界面 + root.title("旅行商问题") # 设置标题 + root.geometry("1400x900") # 设置大小 + + root.resizable(0, 0) # 设置不能调整显示边框 + element_reading() + frame_top = tk.Frame(root, bg='white', width=1400, height=50, highlightthickness=0) + frame_top.place(x=0, y=0) # 绘制信息输出栏 + + frame_left = tk.Frame(root, bg='#FFFAF0', highlightthickness=0, width=500, height=850) # 左边栏 + frame_left.place(x=0, y=50) # 左边栏 + + cv = tk.Canvas(root, bg='#FFFAF0', bd=0, relief="flat", width=900, height=850, highlightthickness=0) + cv.place(x=500, y=50) # 放置绘图Canvas画布 + + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) # 背景 + + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + window(2) + G.lab_show(frame_top, 0, 0, 0) # 显示边信息 + root.mainloop() diff --git a/X4_4.py b/X4_4.py new file mode 100644 index 0000000..82b0721 --- /dev/null +++ b/X4_4.py @@ -0,0 +1,1730 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X4.py +from tkinter import Tk +import random +import math +import time +import tkinter as tk +from tkinter import * +from tkinter import ttk +from tkinter import messagebox +import numpy as np +import networkx as nx +from PIL import Image, ImageTk + +MODE = 0 +BLUE = "#0080FF" +BLACK = "#000000" +RED = "#FFAAAA" +YELLOW = "#FFFACD" +LINE = '#c8d2c8' +GREY = '#070b19' +GREEN = '#5ba585' +NODE = '#33a8cd' +ZERO = 'gold' +MARK_NODE = 0 +Specify_Nodes = '' +Edges_list = [] +NAME = [] +CLICK = [0, 0, 0] + +List_Image = [] + +class Data: + def __init__(self, source: int, num: int, mold: int): + global NAME + if num <= 0 or num > 10: + raise ValueError("num must be a positive integer") + self.styple = mold + self.edgeinfo = 1 # 是否显示详情 + self.nodes_num = num # 节点个数 + self.ang = 360 / self.nodes_num # 圆心顶点角度 + self.R = 300 # 外接圆半径 + self.bc = 2 * self.R * math.sin(math.pi / self.nodes_num) # 节点之间的正多边形距离 + self.canvas_len = int(2 * self.R + 80) # 画布边长 + self.center = ((self.canvas_len+220) // 2, (self.canvas_len+230) // 2) # 画布中心点坐标 + self.drop = [] + self.index = 0 + if source == 3 or source == 2: # 指定删除 + # name = [str(i) for i in range(1, num + 2)] + # print(name) + if source == 2: + NAME.pop() + else: + # print(Specify_Nodes) + NAME.remove(Specify_Nodes) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_delete(source) # 创建第一条连接 + else: + if source == 1: + name = str(int(NAME[-1])+1) + # print(name) + NAME.append(name) + else: + NAME = [str(i) for i in range(1, num + 1)] + # print(NAME) + self.name = NAME + self.coordinate = self.coord_creat() # 纯节点坐标 + self.Nodes = self.nodes_creat() # 创建节点列表 + # print(self.Nodes) + self.Edges = self.edges_creat(source) # 创建第一条连接 + self.edge_add(2, source) # 创建第2条连接 + self.edge_add(3, source) # 创建第3条连接 + + def nodes_creat(self, n_sum=None): + if n_sum == None: + n_sum = self.nodes_num + nodes = [] # 初始化node表 + # 设置画布中心点坐标x0,y0 + x0 = self.center[0] + y0 = self.center[1] + for i in range(n_sum): # 通过几何运算得到多边形各个顶点坐标 + rad = math.radians(i * self.ang) # 计算第i个点的弧度 + x = int(math.cos(rad) * self.R) # 计算第i个顶点x坐标 + y = int(math.sin(rad) * self.R) # 计算第i个顶点y坐标 + # name = '' + str(i + 1) # 给第i个顶点命名 + name = '' + self.name[i] + mark = 0 # 节点为未标记 + dominator = 1 # 设置为必经节点 + nodes.append([i, name, mark, (x0 + x, y0 + y), dominator]) # 将当前节点加入node中 + return nodes + + def edges_delete(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 + # print(str(int(Specify_Nodes) + 1)) + for list_edge in Edges_list: + # print(list_edge) + edges.append([ser, list_edge[1], list_edge[2],list_edge[3], list_edge[4],list_edge[5], list_edge[6], + list_edge[7], list_edge[8]]) # 将连接加入到边的集合中 + nox = [int(num) for num in edges[ser][5].split('-')] + # print(nox) + if len(nox) == 2: + ss = edges[ser][5] + else: + ss = edges[ser][5][:-2] + # print(ss) + if source == 2: + if str(len(self.Nodes)+1) in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + else: + if Specify_Nodes in ss: + # print(edges[ser]) + edges.pop() + ser -= 1 + ser += 1 + return edges + + def edges_creat(self, source): + edges = [] # 初始化所有链接集合 + ser = 0 # 初始化链接对象编号 + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + # print(nodes1) + self.drop = [] + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + # print(nodes1) + for node1 in nodes1: # 遍历删除后的节点信息 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + if self.styple == 1: + dist_c = random.randint(1, 99) # 随机生成距离成本 + time_c = random.randint(1, 99) # 随机生成链接的时间 + else: + if self.drop.count(n2) > 3 or self.drop.count(n1) > 3: + dist_c = random.randint(101, 120) # 随机生成距离成本 + time_c = random.randint(101, 120) # 随机生成链接的时间 + else: + dist_c = random.randint(10, 120) # 随机生成距离成本 + time_c = random.randint(10, 120) # 随机生成链接的时间 + seq = 1 # 链接序号 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}" # 链接标签 + enable = 1 # 连接是否可用 + if dist_c >= 100 or time_c >= 100: # 设置某些节点为不显示 + enable = 0 + else: + self.drop.append(n1) + self.drop.append(n2) + if source == 1: + # print(n2, len(self.Nodes)) + if n2 == len(self.Nodes)-1: + # print('进来了') + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + else: + # print(self.index) + edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5],Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + self.index += 1 + else: + edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, enable, mark, tag, dist_c, time_c, seq]) # 将连接加入到边的集合中 + ser += 1 # 计数加一 + return edges + + def edge_add(self, no, source): + nodes = self.Nodes.copy() # 复制节点对象 + nodes1 = self.Nodes.copy() # 复制节点对象 + for node in nodes: # 遍历节点对象 + if node in nodes1: # 删除nodes1中当期遍历的节点信息 + nodes1.remove(node) + for node1 in nodes1: # 遍历删除后的节点信息 + ser = len(self.Edges) # 链接序号 + n1 = node[0] # 节点对象编号1 + n2 = node1[0] # 节点对象编号2 + show = 0 # 设置为不显示 + mark = False # 连接是否标记 + tag = f"{NAME[n1]}-{NAME[n2]}-{no}" # 链接标签 + dist_c = random.randint(30, 100) # 距离成本 + time_c = random.randint(1, 30) # 链接的时间 + enable = no # 连接是否可用 + if source == 1: + if n2 == len(self.Nodes)-1: + # print('进来了') + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) # 将连接加入到边的集合中 + else: + # print(no ,self.index) + self.Edges.append([ser, Edges_list[self.index][1], Edges_list[self.index][2], + Edges_list[self.index][3], Edges_list[self.index][4], + Edges_list[self.index][5], Edges_list[self.index][6], + Edges_list[self.index][7], Edges_list[self.index][8]]) # 将连接加入到边的集合中 + + self.index += 1 + else: + self.Edges.append([ser, int(NAME[n1])-1, int(NAME[n2])-1, show, mark, tag, dist_c, time_c, enable]) + + def coord_creat(self): + '''返回每个节点的坐标''' + coordinate = [] + x0 = self.center[0] + y0 = self.center[1] + for i in range(self.nodes_num): + rad = math.radians(i * self.ang) + x = int(math.cos(rad) * self.R) + y = int(math.sin(rad) * self.R) + coordinate.append((x0 + x, y0 + y)) + return coordinate + + def node_co(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[3] + + def node_no(self, key): # 通过节点编号返回编号的坐标 + """通过节点编号返回编号的坐标""" + nodes = self.Nodes + for n in nodes: + if n[1] == str(key + 1): + return n[0] + def node_coordinate(self, n): # 通过节点编号返回矩阵的坐标 + result = [] + for i in range(n): + for j in range(i + 1, n): + result.append((i, j)) + return result + +# G = Graph() +# print(1) +nodes = 6 # 设置初始节点数量 +d = Data(0, nodes, 0) + +class Graph: + # Graph定义 + def edge_info(self, cv: tk.Canvas, info: str, n1: tuple, n2: tuple): # 显示路径信息 + if d.edgeinfo == 0: # 若设置为不显示则直接返回 + return + # print(n1) + # print(n2) + x = (n1[0] - n2[0]) // 2 + n2[0] # 计算中点x坐标 + y = (n1[1] - n2[1]) // 2 + n2[1] # 计算中点y坐标 + if (n2[1] - n1[1]) != 0: # 斜率为非0度时的显示 + reat = math.atan((n2[0] - n1[0]) / (n2[1] - n1[1])) # 根据斜率计算弧度 + ang = round(math.degrees(reat)) + 90 # 根据弧度转角度 + if ang > 89: # 斜度大于90度调整坐标 + text_x, text_y = x + 15, y + else: # 斜度大于90度调整坐标 + text_x, text_y = x, y + 15 + else: # 斜率为0度时的显示 + ang = round(math.degrees(0)) # 根据弧度转角度 + text_x, text_y = x - 30, y + 10 + # print(info.split(' ')[-1]) + cv.create_text(text_x, text_y, text=f'{info.split()[-2]}', fill="black", font=('微软雅黑', 12), angle=ang) # 根据信息显示文字 + + def draw(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + dis, tim = self.edges_show(cv, d.Edges) # 绘制边 + self.lab_show(frame_top, len(d.Nodes), tim, dis) # 显示边信息 + self.nodes_show(cv) # 绘制点 + cv.update() # 更新画布 + + + def lab_show(self, frame: tk.Frame, nodes: int, time: int, distance: int): + labs = ["节点数目", "时间", "距离(成本)"] + x0 = d.canvas_len // 3 + for i in range(len(labs)): + lab1 = tk.Label(frame, text=labs[i], fg=BLACK, bg='white', font=('微软雅黑', 12)) + if i == 0: + lab2 = tk.Label(frame, text=f"{nodes}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + elif i == 1: + lab2 = tk.Label(frame, text=f"{time}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + else: + lab2 = tk.Label(frame, text=f"{distance}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(x=10 + x0 * i, y=10, width=80, height=30) + lab2.place(x=10 + x0 * i + 80, y=10, width=80, height=30) + lab1 = tk.Label(frame, text='分钟', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0, y=10, width=50, height=30) + lab1 = tk.Label(frame, text='千米', fg=BLACK, bg='white', font=('微软雅黑', 12)) + lab1.place(x=170 + x0 * 2, y=10, width=50, height=30) + + def nodes_show(self, cv: tk.Canvas): + COL = NODE # 初始化节点颜色 + for i in d.Nodes: # 遍历每一个节点 + x, y = i[3] # 记录该节点坐标 + if i[2] == 1 or i[2] == 2: # 如果节点被标记,则将节点标为红色 + COL = RED + elif i[0] == 0: # 如果节点为起始节点,则将节点标记为特殊颜色 + COL = ZERO + elif i[2] == 5: # 如果节点已经被访问,则将节点标记(用户路径功能) + COL = 'gold' + else: # 否则则使用初始颜色 + COL = NODE + if i[4] == 1: # 如果是必经节点,则加黑色边框 + w = 1 + else: # 如果是非必经节点,则没有边框 + w = 0 + # 根据坐标,圆的颜色与是否有边框绘制圆圈来表示节点 + cv.create_oval(x - 20, y - 20, x + 20, y + 20, fill=COL, width=w, ) # outline='#ee28a3') + cv.create_text(x, y, text=f'{i[1]}', fill=GREY, font=('华文楷体', 18)) + cv.update() + + def edges_show(self, cv: tk.Canvas, edges: d.Edges): + """ + 显示链接 + :param cv: tkcanvas对象 + :return: + """ + distance = 0 # 初始化被标记的线的总路径 + time1 = 0 # 初始化被标记的线的总时间 + for edge in edges: + node_1 = d.node_co(edge[1]) # 链接线编号对应的坐标1 + node_2 = d.node_co(edge[2]) # 链接线编号对应的坐标2 + # print(node_1,node_2) + if edge[8] == 2 and edge[3] == 1: # 若该链接为两点间第二条连线 + self.more_edge_show(2, edge) + elif edge[8] == 3 and edge[3] == 1: # 若为两点间第3条连线 + self.more_edge_show(3, edge) + if edge[8] == 1 and edge[3] == 1: # 若为两点间第1条连线且该连接存在 + self.edge_info(cv, f'{edge[5]} {edge[6]} {edge[7]}', node_1, node_2) + point = [node_1, node_2] + if edge[4] == 1 and edge[8] == 1 and edge[3] == 1: # 如果路径被标记,则用绿色绘制 + if MODE == 1: + cv.create_line(point, fill=RED, width=4) + elif MODE == 2: + cv.create_line(point, fill=BLUE, width=4) + else: + cv.create_line(point, fill=GREEN, width=4) + distance += edge[6] # 更新总路径 + time1 += edge[7] # 更新总时间 + elif edge[8] == 1 and edge[3] == 1: # 如果是节点之间的第一条线,则用正常颜色标记 + cv.create_line(point, fill=LINE, width=2) + if d.Nodes[d.node_no(edge[2])][2] + d.Nodes[d.node_no(edge[2])][2] == 3: # 若该线被用户选中,则用蓝色虚线标记 + cv.create_line(point, fill=BLUE, width=5, dash=(4, 14)) + return (distance, time1) # 返回标记路径的总时间与路程 + + # def node_edge(self, a, b): # 根据点的编号找边的编号 + # if a > b: + # x = a + # a = b + # b = x + # return int(a * (2 * len(d.Nodes) - a - 3) / 2 + b - 1) + + def node_edge(self, a, b): # 根据点的编号找边的编号 + for edge in d.Edges: + if a == edge[1] and b == edge[2]: + return edge[0] + if b == edge[1] and a == edge[2]: + return edge[0] + + def way_node_edge(self, a, b): # 根据点的编号找边的编号 + way = [] + for node in d.Nodes: + way.append(int(node[1])) + # print(way) + # print(a, b) + x, y = way[a]-1, way[b]-1 + for edge in d.Edges: + if x == edge[1] and y == edge[2]: + return edge[0] + if y == edge[1] and x == edge[2]: + return edge[0] + + def delete_all(self): + cv.delete('all') # 清空画布 + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) + self.lab_show(frame_top, 0, 0, 0) # 显示边信息 + W.user_way = [0] + window(2) + +class Edge: + def __init__(self): + self.mark = [] + # 记录已标记的节点数目(连接操作) + self.user_cur = 0 + # 记录用户所在点 + + def node_edge(self, no, n1, n2): # 根据节点返回编号 + if n1 > n2: + n = n1 + n1 = n2 + n2 = n + for e in d.Edges: + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 + def edge_del_all(self, mode): + # mode = 1:优先最短路径 + # mode = 2:优先最短时间 + # cur1 = node_edge() + if mode == 1: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_cost(e[1], e[2]) + else: + for e in d.Edges: + if (e[8] == 2 and e[3] == 1) or (e[8] == 3 and e[3] == 1): + self.edge_merge_time(e[1], e[2]) + G.draw() + + def edge_merge_cost(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][6]), (cur2, d.Edges[cur2][6]), (cur3, d.Edges[cur3][6])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + def edge_merge_time(self, n1, n2): + cur1 = node_edge(n1, n2) # 初始化三条可能的边的编号 + cur2 = node_edge(n1, n2, 2) # 初始化三条可能的边的编号 + cur3 = node_edge(n1, n2, 3) # 初始化三条可能的边的编号 + edges = [(cur1, d.Edges[cur1][7]), (cur2, d.Edges[cur2][7]), (cur3, d.Edges[cur3][7])] # 提出编号与路径花费 + valid_edges = [(edge, cost) for edge, cost in edges if d.Edges[edge][3] == 1] # 过滤出存在的边 + valid_edges.sort(key=lambda x: x[1]) # 按照 cost 排序,保留 cost 最小的边 + for idx, (edge, _) in enumerate(valid_edges): + d.Edges[edge][8] = idx + 1 # 设置边的优先级 + d.Edges[edge][0] = edge # 确保边的索引正确 + for edge, _ in valid_edges[1:]: # 对于非最优边,设置其为不显示 + d.Edges[edge][3] = 0 + +class Solve: + def __init__(self): + self.ans = [] # 记录答案 + self.mutians = [] # 若有多个最短路,则保存在mutians中 + self.way_sum = 0 # 记录遍历的路径数目 + self.minn = float("inf") # 初始化最小值为正无穷 + self.start = 0 # 起始点 + self.end = 0 # 终点 + self.mark = [] # 记录该点是否已经经过 + self.user_cur = 0 # 记录用户所在点 + self.cura = -1 # 记录用户选择的第一个点 + self.curb = -1 # 记录用户选择的第二个点 + self.con = 0 # 记录必经的节点数目 + + def node_greedy(self): # 贪心 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.greedy(distances) + self.ans = ways.copy() + # 如果找到的路径不合法,则表示贪心法无法求解得到合法路径 + print(self.way_sum) + # for i in distances: + # print(i) + if len(ways) != l+1 : + tk.messagebox.askokcancel(title='结果', message='该图不存在贪心算法路径') + print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.ans.clear() + self.end = time.time() # 记录结束时间 + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + # print(ways[len(ways) - 1], i) + # d.Edges[G.way_node_edge(ways[len(ways) - 1], i)][4] = True # 标记起点终点形成环线 + # print(ways) + tk.messagebox.askokcancel(title='结果', message='贪心算法路径') + self.end = time.time() # 记录结束时间 + def greedy(self,distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + self.way_sum += 1 # 遍历路径的数目加一 + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + # print(path) + return path + + def tsp_backtracking(self): + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = 0 # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + # print(l) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + # print(distances) + ways = self.backtracking(distances) + print(self.way_sum) + + if ways: # 判断路径是否存在 + self.ans = ways.copy() + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='回溯算法路径') + self.end = time.time() # 记录结束时间 + else: + tk.messagebox.askokcancel(title='结果', message='该图不存在回溯算法路径') + self.end = time.time() # 记录结束时间 + return -1 + + def backtracking(self, distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + self.way_sum += 1 # 遍历路径的数目加一 + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + + backtrack(0, 0, visited, path) + return shortest_path + + def auto_solve(self, number): + global MODE, MARK_NODE + MARK_NODE = 0 + MODE = 2 + if number == 1: # 回溯 + self.tsp_backtracking() + elif number == 2: + self.node_greedy() # 贪心 + else: # 深度优先算法 + self.node_dfs() + window(1) + G.draw() + + def node_dfs(self): # 遍历 深度优先算法 + self.start = time.time() # 记录开始时间 + self.way_sum = 0 + self.ans.clear() # 初始化答案列表 + E.edge_del_all(1) # 两点间仅保留距离最短路径 + for edge in d.Edges: # 清空标记 + edge[4] = False + i = 0 # 起始点 + flag = [0] * len(d.Nodes) # 初始化已经过点 + cur = i # 初始化起始点 + flag[cur] = 1 # 将起始点标记为已经过 + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 每一步选择当前最短的链接加到路径中去 + if j[0] >= len(result): + break + else: + if j[3] == 1: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + cost, ways = self.dfs(distances) + print(self.way_sum) + self.ans = ways.copy() + if len(ways) != l + 1: + tk.messagebox.askokcancel(title='结果', message='该图不存在dfs路径') + # print(ways) + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + self.end = time.time() # 记录结束时间 + self.ans.clear() + return -1 + for way in range(len(ways) - 1): # 标记一条路径 + d.Edges[G.way_node_edge(ways[way], ways[way + 1])][4] = True + tk.messagebox.askokcancel(title='结果', message='dfs算法路径') + self.end = time.time() # 记录结束时间 + + + def dfs_tsp(self,graph, start, current, path, + visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[ + current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and \ + not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + self.way_sum += 1 # 遍历路径的数目加一 + self.dfs_tsp(graph, start, next_node, path, + visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + def dfs(self, graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + self.dfs_tsp(graph, start_node, start_node, path, + visited, cost, min_cost, min_path) + return min_cost[0], min_path[0] + + def find_hamiltonian_cycles(self,start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == np.inf: + return False + if node in path: + return False + return True + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != np.inf: + cycles.append(path + [start]) + path.pop() + find_paths(start) + return cycles + def check(self): + l = len(d.Nodes) + distances = [[math.inf for _ in range(l)] for _ in range(l)] + result = d.node_coordinate(l) + for j in d.Edges: # 将路径中的 + if j[0] >= len(result): + break + else: + if j[6] < 99 and j[7] < 99: + distances[result[j[0]][0]][result[j[0]][1]] = j[6] + distances[result[j[0]][1]][result[j[0]][0]] = j[6] + all_hamiltonian_cycles = self.find_hamiltonian_cycles(0, np.array(distances)) + l = len(all_hamiltonian_cycles) + if l: + for cycles in all_hamiltonian_cycles: + dis_ans = 0 + for i in range(d.nodes_num): + dis_ans += d.Edges[G.way_node_edge(cycles[i], cycles[i + 1])][6] + print(cycles,dis_ans) + if all_hamiltonian_cycles: + tk.messagebox.askokcancel(title='结果', message=f'该图存在 {len(all_hamiltonian_cycles)} 条路径\n') + else: + tk.messagebox.askokcancel(title='结果', message='该图不能完全互通') + +def node_edge(n1, n2, no=1): # 根据点的编号找边的编号 + if n1 > n2: # 为了防止充分编号,让n1小于n2 + n1, n2 = n2, n1 + for e in d.Edges: # 遍历所有路径,找到符合要求的返回 + if e[1] == n1 and e[2] == n2 and e[8] == no: + return e[0] + return -1 # 若找不到,则返回-1 + +class Ways(): + def __init__(self): + self.user_way = [0] # 记录用户路径 + self.user_cur = 0 # 记录用户所在点 + self.user_flag = set() # 记录用户已经过的点 + def auto_ways(self): # 标记用户路径 + for w in d.Edges: # 清空路径标记 + w[4] = 0 + for N in d.Nodes: # 清空节点标记 + N[2] = 0 + for w in self.user_way: # 标记用户已选的点 + d.Nodes[w][2] = 5 + # 标记用户经过的路径 + for w in range(len(self.user_way) - 1): + d.Edges[G.node_edge(self.user_way[w],self.user_way[w + 1])][4] = 1 + # 如果已经访问所有节点,自动回到初始节点 + if len(set(self.user_way)) == len(d.Nodes): + d.Edges[G.node_edge(self.user_way[len(self.user_way) - 1], 0)][4] = 1 + G.draw() # 按照标记重新绘制图形 + + def check_user_best(self): # 检查用户路径是不是最佳路径 + s.node_dfs() + if not len(s.ans): + return -1, 0 + # 长度不一样肯定不匹配 + if not len(self.user_way) == len(s.ans): + return 0, 0 + dis_user = 0 + dis_ans = 0 + num = len(self.user_way) + for i in range(num - 1): + dis_user += d.Edges[G.node_edge(self.user_way[i], self.user_way[i+1])][6] + dis_ans += d.Edges[G.way_node_edge(s.ans[i], s.ans[i + 1])][6] + return dis_ans, dis_user + + def show_best_ans(self, top, best, user): + lab1 = tk.Label(top, text="最短路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + # print(s.ans) + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + lab2 = tk.Label(top, text=f"最佳路径总成本:{best}\n用户路径总成本:{user}", bg=BLUE, fg=BLACK, + font=('微软雅黑', 12), wraplength=200, justify='left') + lab2.place(relx=0.4, rely=0.7 + y0, width=200, height=100) + + def show_BEST(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=140, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + def servey_path_display(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way = b_way[:-2] + lab2 = tk.Label(frame_left, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + def check_user_greey(self): # 检查用户路径是不是贪心路径,不是的话返回错误点 + s.node_greedy()# 计算贪心路径 + if not len(s.ans): + return -2 + # 路径长度不一样肯定不匹配,直接返回 + if not len(self.user_way) == len(s.ans): + # self.auto_ways() + return 0 + way = [(int(node[1])-1) for node in d.Nodes] + result1 = list(map(str, self.user_way)) + result1 = ' '.join(result1) + ways = [way[an] for an in s.ans] + result2 = list(map(str, ways)) + result3 = ' '.join(result2) + result2.reverse() # 倒序排列列表元素 + result4 = ' '.join(result2) + if result4 == result1 or result3 == result1: + return -1 #-1表示是贪心路径 + else: + x_1 = self.find_longest(result1, result4) + x_2 = self.find_longest(result1, result3) + return max(x_1, x_2) + + def find_longest(self, list1, list2): + index = 0 + while index < len(list1) and index < len(list2): + if list1[index] != list2[index]: + break + index += 1 + return index + + + def show_greddy_ans(self, top): + lab1 = tk.Label(top, text="贪心路径:", fg=BLACK, font=('微软雅黑', 20)) + b_way = "" + for a in s.ans: + b_way += d.Nodes[a][1] + b_way += "->" + b_way += d.Nodes[0][1] + y0 = 0.05 + lab2 = tk.Label(top, text=f"{b_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, justify='left') + lab1.place(relx=0.1, rely=0.50 + y0, width=150, height=30) + lab2.place(relx=0.4, rely=0.45 + y0, width=200, height=100) + + # 对用户路径进行标记与记录 + def way_route_markings(self, event): + global MARK_NODE + MARK_NODE = 2 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + + left_window() + self.user_way = [0] + # lab = tk.Label(frame_left, text="路径操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.02, width=200, height=30) + y0 = 0.1 + global entry1,entry2,default_value + # 定义默认值变量 + default_value = tk.StringVar() + default_value.set(self.user_way[-1]+1) + laba1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + laba1.place(relx=0.2, rely=0.28 + y0, width=140, height=30) + # 创建第一个输入框 + entry1 = tk.Entry(frame_left, textvariable=default_value, bg="#F5F5F5") + entry1.place(relx=0.6, rely=0.28 + y0, width=50, height=30) + labb1 = tk.Label(frame_left, text="当前下一节点:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb1.place(relx=0.2, rely=0.33 + y0, width=140, height=30) + # 创建第二个输入框 + entry2 = tk.Entry(frame_left, bg="#F5F5F5") + entry2.place(relx=0.6, rely=0.33 + y0, width=50, height=30) + + labb3 = tk.Label(frame_left, text="是否进行连接:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + labb3.place(relx=0.2, rely=0.4 + y0, width=140, height=30) + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[38], bg='white', command=lambda: self.extract_values()) # 确定 + button.place(relx=0.55, rely=0.38 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[36], bg='white', command=lambda: self.revoke()) # 撤销 + button2.place(relx=0.18, rely=0.46 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[37], bg='white', command=lambda: self.clear_all_paths()) # 清除路径 + button3.place(relx=0.5, rely=0.46 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[42], bg='white', command=lambda: self.greedy_path()) # 判断路径是否为贪心路径 + button4.place(relx=0.18, rely=0.55 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[43], bg='white', command=lambda: self.shortest_path()) # 判断路径是否为最短路径 + button5.place(relx=0.18, rely=0.62 + y0) + + self.user_path_display() + def extract_values(self): + global entry1, entry2, default_value + global MARK_NODE + MARK_NODE = 2 + value1 = entry1.get() # 获取第一个输入框的值 + value2 = entry2.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + E = 0 + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + for item in d.Edges: + if str_route == item[5]: + if item[6] < 100 and item[7] < 100: + if d.Edges[G.node_edge(value1-1, value2-1)][4]: + E = 2 + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + else: + if self.user_way[-1] == (value1-1): + E = 1 + global MODE + MODE = 1 + self.user_way.append(value2 - 1) + default_value.set(value2) + d.Edges[G.node_edge(value1-1, value2-1)][4] = True + entry1.config(textvariable=default_value) # 更新entry1的默认值 + entry2.delete(0, tk.END) + else: + E = 3 + tk.messagebox.askokcancel(title='错误', message='未连贯!') + break + print(E) + if E == 1: + G.draw() + self.user_path_display() + elif E == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def extract_ligature(self, n1, n2): + global MARK_NODE + MARK_NODE = 2 + no = G.way_node_edge(n1, n2) + flage = False + if d.Edges[no][4]: + tk.messagebox.askokcancel(title='错误', message='城市已连接!') + elif d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='错误', message='城市未连接!') + else: + if self.user_way[-1] == int(d.Nodes[n2][1]) - 1: + nx = n1 + n1 = n2 + n2 = nx + if self.user_way[-1] == int(d.Nodes[n1][1])-1: + global MODE + MODE = 1 + self.user_way.append(int(d.Nodes[n2][1]) - 1) + d.Edges[no][4] = True + flage = True + else: + tk.messagebox.askokcancel(title='错误', message='未连贯!') + if flage: + G.draw() + self.user_path_display() + + def revoke(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1+1}-{value2+1}' + else: + str_route = f'{value2+1}-{value1+1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 1 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2+1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + G.draw() + self.user_path_display() + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def clear_all_paths(self): + global MARK_NODE + MARK_NODE = 2 + try: + if len(self.user_way) > 1: + for i in range(len(self.user_way)-1): + value1 = int(self.user_way[-1]) # 将值转换为整数 + value2 = int(self.user_way[-2]) # 将值转换为整数 + if value1 < value2: + str_route = f'{value1 + 1}-{value2 + 1}' + else: + str_route = f'{value2 + 1}-{value1 + 1}' + # print(str_route) + for item in d.Edges: + # print(item) + if str_route == item[5]: + # print(item) + global MODE + global entry1, default_value + MODE = 3 + self.user_way.pop() + print(self.user_way) + d.Edges[G.node_edge(value1, value2)][4] = False + default_value.set(value2 + 1) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + break + else: + tk.messagebox.askokcancel(title='提示', message='未进行路径建设!') + for i in range(len(d.Edges)): + d.Edges[i][4] = False + G.draw() + self.user_path_display() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def greedy_path(self): + g = self.check_user_greey() + if g == -2: + # cvx.create_text(0, 0, text="不存在贪心路径") + lab1 = tk.Label(frame_left, text="不存在贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + elif g == -1: # 如果用户路径是贪心路径则显示 + # cvx.create_text(0, 0, text="用户路径是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + # elif g: + # self.servey_greedy_path() + else: + # print(self.user_way) + # print(self.user_way[0:g]) + # cvx.create_text(0, 0, text="用户路径不是是贪心路径") + lab1 = tk.Label(frame_left, text="用户路径不是是贪心路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.3, rely=0.8, width=180, height=30) + + def servey_greedy_path(self): + lab1 = tk.Label(frame_left, text="最短路径:", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.1, rely=0.75, width=80, height=30) + # self.auto_ways() + g_way = '' + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + lab2 = tk.Label(frame_left, text=f"{g_way}", bg=BLUE, fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.1, rely=0.8, width=200, height=100) + + + + def shortest_path(self): + best, user = self.check_user_best() # 计算最优路径与用户路径的总花费 + if not best: + lab1 = tk.Label(frame_left, text="不存在最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + elif best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + else: + lab1 = tk.Label(frame_left, text="用户路径是不最短路径", bg='white', fg=BLACK, font=('微软雅黑', 12)) + lab1.place(relx=0.33, rely=0.8, width=180, height=30) + # if best == user: # 如果最优路径与用户路径的总花费相等,则说明用户路径是最短路径 + # lab1 = tk.Label(frame_left, text="用户路径是最短路径", bg=RED, fg=BLACK, font=('微软雅黑', 12)) + # lab1.place(relx=0.1, rely=0.8, width=180, height=30) + # else: # 否则说明用户路径不是最短路径 + # self.servey_path_display() + + # 用户路径展示 + def user_path_display(self): + lab1 = tk.Label(frame_left, text="用户路径:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + lab1.place(relx=0.15, rely=0.8, width=80, height=30) + # self.auto_ways() + way = "" + # d_way = [] + # for i in d.Nodes: + # d_way.append(i[1]) + for i in range(len(self.user_way)): + if len(self.user_way)-i > 1: + way += str(self.user_way[i]+1) + way += "->" + else: + way += str(self.user_way[i]+1) + lab2 = tk.Label(frame_left, text=f"{way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(relx=0.18, rely=0.84, width=300, height=100) + + def way_operate(self, event): + global MARK_NODE + for i in range(len(d.Edges)): + d.Edges[i][4] = False + MARK_NODE = 5 + # E.newedge_del_all(1) + for w in frame_left.winfo_children(): + w.destroy() + # 清除左侧组件 + left_window() + self.user_way = [0] + y0 = 0.1 + label1 = tk.Label(frame_left, text="节点1:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label1.place(relx=0.15, rely=0.32 + y0, width=100, height=30) + global entry3, entry4 + # 创建第一个输入框 + entry3 = tk.Entry(frame_left, bg='#F5F5F5') + entry3.place(relx=0.6, rely=0.32 + y0, width=50, height=30) + + label2 = tk.Label(frame_left, text="节点2:", bg='white', fg=BLACK, font=('微软雅黑', 15)) + label2.place(relx=0.15, rely=0.37 + y0, width=100, height=30) + # 创建第二个输入框 + entry4 = tk.Entry(frame_left, bg='#F5F5F5') + entry4.place(relx=0.6, rely=0.37 + y0, width=50, height=30) + button1 = tk.Button(frame_left, image=List_Image[45], relief="flat", bg='white', + command=lambda: self.complete_graph()) # 完全图 + button1.place(relx=0.15, rely=0.43 + y0) + button2 = tk.Button(frame_left, image=List_Image[46], relief="flat", bg='white', + command=lambda: self.reachable_judgment()) # 节点可达性判断 + button2.place(relx=0.5, rely=0.43 + y0) + + + label3 = tk.Label(frame_left, text="是否进行两节点之间操作:", bg='white', fg=BLACK, font=('微软雅黑', 14)) + label3.place(relx=0.15, rely=0.5 + y0, width=240, height=30) + # 按钮 + button3 = tk.Button(frame_left, relief="flat", image=List_Image[33], bg='white', + command=lambda: self.way_add()) # 线路增加 + button3.place(relx=0.2, rely=0.56 + y0) + button4 = tk.Button(frame_left, relief="flat", image=List_Image[34], bg='white', + command=lambda: self.way_change()) # 线路修改 + button4.place(relx=0.2, rely=0.66 + y0) + button5 = tk.Button(frame_left, relief="flat", image=List_Image[35], bg='white', + command=lambda: self.way_delete()) # 线路删除 + button5.place(relx=0.2, rely=0.76 + y0) + + def is_reachable(self, graph, start, target, visited): + if start == target: + return True + visited.add(start) + for neighbor in graph[start]: + if neighbor not in visited: + if self.is_reachable(graph, neighbor, target, visited): + return True + return False + + def complete_graph(self): + l = len(d.Nodes) + result = d.node_coordinate(l) + for index in range(len(result)): + if d.Edges[index][3] == 0: + dist_c = random.randint(10, 99) # 随机生成距离成本 + time_c = random.randint(10, 99) # 随机生成链接的时间 + d.Edges[index][3] = 1 + d.Edges[index][6] = dist_c + d.Edges[index][7] = time_c + G.draw() + + + def reachable_judgment(self): + try: + a = int(entry3.get()) # 获取第一个输入框的值 + b = int(entry4.get()) # 获取第二个输入框的值 + l = len(d.Nodes) + index_list = [] + for i in d.Nodes: + index_list.append(i[1]) + dictionary = {i: [] for i in range(1, l + 1)} + result = d.node_coordinate(l) + # 生成无向图 + for i in range(len(result)): + if d.Edges[i][3] == 1: + x = d.Edges[i][5].split('-')[0] + y = d.Edges[i][5].split('-')[-1] + dictionary[index_list.index(x)+1].append(index_list.index(y)+1) + dictionary[index_list.index(y)+1].append(index_list.index(x)+1) + start_node = index_list.index(str(a))+1 + target_node = index_list.index(str(b))+1 + visited_nodes = set() + print(dictionary) + flag = self.is_reachable(dictionary, start_node, target_node, visited_nodes) + if flag: + tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}可达') + else: + tk.messagebox.askokcancel(title='结果', message=f'{a}到{b}不可达') + except ValueError: + tk.messagebox.askokcancel(title='结果', message='请输入节点') + + def way_add(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + if d.drop.count(ways_list.index(value1)) > 4 or d.drop.count(ways_list.index(value2)) > 4: + print(d.drop) + tk.messagebox.askokcancel(title='提醒', message='当前节点已经到达最大连接数!') + return + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 1: + tk.messagebox.askokcancel(title='提醒', message='当前路线已存在!') + return + top = creat_window(str_route+'路径参数配置') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", 101) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", 101) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1,result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][3] = 1 + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_change(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + result2 = int(time_entry.get()) + print(result1, result2) + if 100 > result1 > 0 and 0 < result2 < 100: + d.Edges[no][6] = result1 + d.Edges[no][7] = result2 + top.destroy() # 关闭窗口 + G.draw() + else: + tk.messagebox.askokcancel(title='错误', message='路程与时间范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的路程与时间!') + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + def way_delete(self): + global entry3, entry4 + value1 = entry3.get() # 获取第一个输入框的值 + value2 = entry4.get() # 获取第二个输入框的值 + try: + value1 = int(value1) # 将值转换为整数 + value2 = int(value2) # 将值转换为整数 + print("值1:", value1) + print("值2:", value2) + ways_list = [int(node[1]) for node in d.Nodes] + # 判断两点是否连接 + if abs(value2) > 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + d.Edges[no][3] = 0 + d.Edges[no][6] = 111 + d.Edges[no][7] = 111 + G.draw() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') + + +class Node: + # 标记节点按钮 + def node_mark_display(self, event): + global MARK_NODE, Specify_Nodes + W.user_way = [0] + MARK_NODE = 1 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + curx = -1 + y0 = 0.1 + for i in d.Nodes: + if i[2] == 1: + curx = i[0] + # lab = tk.Label(frame_left, text="节点操作", bg=RED, fg=BLACK, font=('微软雅黑', 20)) + # lab.place(relx=0.18, rely=0.32, width=200, height=30) + labx1 = tk.Label(frame_left, text=f"当前共有{len(d.Nodes)}个节点", bg='white', fg=BLACK, font=('微软雅黑', 20)) + labx1.place(relx=0.15, rely=0.3 + y0, width=260, height=30) + lab1 = tk.Label(frame_left, text="当前节点:", bg='white', fg=BLACK, font=('微软雅黑', 20)) + if curx == -1: + text = "未选择" + else: + text = f"{d.Nodes[curx][1]}" + Specify_Nodes = str(d.Nodes[curx][1]) + lab2 = tk.Label(frame_left, text=text, bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 20)) + lab1.place(relx=0.15, rely=0.4 + y0, width=160, height=50) + lab2.place(relx=0.6, rely=0.4 + y0, width=100, height=50) + + # 按钮 + button = tk.Button(frame_left, relief="flat", image=List_Image[30], bg='white', + command=lambda: self.node_del_exact()) # 删除当前节点 + button.place(relx=0.2, rely=0.5 + y0) + button2 = tk.Button(frame_left, relief="flat", image=List_Image[32], bg='white', + command=lambda: self.node_add()) # 增加一个节点 + button2.place(relx=0.2, rely=0.6 + y0) + button3 = tk.Button(frame_left, relief="flat", image=List_Image[31], bg='white', + command=lambda: self.node_del()) # 减少一个节点 + button3.place(relx=0.2, rely=0.7 + y0) + + def node_add(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + # 增加一个节点 + nodes = len(d.Nodes) + 1 + # 如果节点数目大于五,则将连接详细信息改为不显示 + if nodes > 5: + d.edgeinfo = 0 + if nodes > 10: + tk.messagebox.askokcancel(title='错误', message='已达到最大节点') + return + d = Data(1, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + + def node_del(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = [] + Edges_list = d.Edges + # print(len(Edges_list)) + if d.nodes_num <= 2: + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(2, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + def node_del_exact(self): + global d,Edges_list + for i in range(len(d.Edges)): + d.Edges[i][4] = False + Edges_list = d.Edges + if len(Specify_Nodes) == 3: + tk.messagebox.askokcancel(title='错误', message='未选择节点') + return + if d.nodes_num <= 2:# 若删除节点以后节点过少,则直接返回并警告 + tk.messagebox.askokcancel(title='错误', message='节点过少') + return + nodes = len(d.Nodes) - 1 + if nodes < 6: + d.edgeinfo = 1 + d = Data(3, nodes, d.styple) + # for i in d.Edges: + # print(i) + G.draw() + self.node_mark_display(1) + + +def refresh(): + top = creat_window('路径生成') # 创建弹出窗口 + distance_entry = create_input_box(top, "节点个数:(1-10)", 6) # 创建一个输入框,获取节点个数 + # 创建一个下拉框,用于选择生成图类型 + mode_combobox = create_dropdown_box(top, "生成图类型", ["TSP", "完全图"]) # 创建一个下拉框,用于选择生成图类型 + + def get_input(): # 创建 + # 获取输入框的内容 + try: + result1 = int(distance_entry.get()) + # 从下拉框中获取池化类型 + result2 = mode_combobox.get() + print(result1, result2) + flag = 0 + if result2!=None: + if result2 == '完全图': + flag = 1 + if 10 > result1 > 0: + global d + if flag: + d = Data(0, result1, 1) + else: + d = Data(0, result1, 0) + top.destroy() # 关闭窗口 + G.draw() + # for i in d.Edges: + # print(i) + else: + tk.messagebox.askokcancel(title='错误', message='节点数范围错误!') + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的节点数') + + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + +def graph_init(): + node = random.randint(4, 10) + global d, MODE, MARK_NODE + MARK_NODE = 0 + MODE = 0 + d = Data(0, node,0) + print(node) + W.user_way = [0] + G.draw() + window(2) + +def empty(): + global MARK_NODE + MARK_NODE = 0 + G.delete_all() + +def auto_opera_mu(menu: tk.Menu): + """TSP问题自动生成与自动求解相关""" + menu_node = Menu(menu, tearoff=False) + menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) + menu_node.add_command(label="重新生成", command=lambda: refresh()) + menu_node.add_command(label="清空", command=lambda: empty()) + menu_node.add_command(label="自动求解最优路径-dfs遍历", command=lambda: s.auto_solve(3)) + menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: s.auto_solve(2)) + menu_node.add_command(label="自动求解优化路径-回溯", command=lambda: s.auto_solve(1)) + menu_node.add_command(label="检查是否存在路径", command=lambda: s.check()) + # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 + menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) + +# 创建弹出窗口 +def creat_window(title): + top = tk.Toplevel(root) + top.geometry("300x350") + top.title(title) + return top + +# 输入框 +def create_input_box(top, text, value): + box_label = tk.Label(top, text=text) + box_label.pack(padx=10, pady=10) + box_size = tk.IntVar(top, value=value) # 创建一个IntVar对象,并设置默认值为3 + box_size_entry = tk.Entry(top, textvariable=box_size) # 关联IntVar对象 + box_size_entry.pack(padx=20, pady=20) + return box_size_entry + +# 下拉框 +def create_dropdown_box(top, text, listvalues): + # 创建一个下拉框,用于选择 + box_mode_label = tk.Label(top, text=text) + box_mode_label.pack(padx=10, pady=10) + box_mode_combobox = ttk.Combobox(top) + box_mode_combobox["values"] = listvalues + box_mode_combobox.pack(padx=20, pady=15) + return box_mode_combobox + +def dir2(x, y): # 计算两点间距离 + return (x[0] - y[0]) * (x[0] - y[0]) + (x[1] - y[1]) * (x[1] - y[1]) + +def left1(event): + global entry1, entry2, default_value, CLICK, entry3, entry4 + # 查找鼠标左键按下时位置是否在某个节点内 + n = -1 + for node in d.Nodes: + if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: + n = node[0] + # n为点击的节点,若没有则为-1 + m = mode.get() # 查看现在操作类型 + if m == 1 and not n == -1: + # 节点操作 + # N.node_mark(n) + if n >= len(d.Nodes): + # 弹出对话框 + tk.messagebox.askokcancel(title='错误', message='该节点不存在') + return + for node in d.Nodes: + if node[0] == n: + node[2] = 1 + else: + node[2] = 0 + G.draw() + if MARK_NODE == 1: # 限制节点操作 + N.node_mark_display(1) + elif MARK_NODE == 2: # 若是MARK_NODE==2,则可以进行路线画图操作 + if CLICK[2] == 0: + CLICK[0] = n + default_value.set(d.Nodes[n][1]) + entry1.config(textvariable=default_value) # 更新entry1的默认值 + elif CLICK[2] == 1: + CLICK[1] = n + W.extract_ligature(CLICK[0], CLICK[1]) + CLICK[2] = -1 + CLICK[2] += 1 + elif MARK_NODE == 5: # 若是MARK_NODE==5,则可以进行节点连接操作 + if CLICK[2] == 0: + CLICK[0] = n + entry3.delete(0, tk.END) + # 设置输入框的值为 节点 + entry3.insert(0, d.Nodes[n][1]) + elif CLICK[2] == 1: + CLICK[1] = n + # W.extract_ligature(CLICK[0], CLICK[1]) + entry4.delete(0, tk.END) + # 设置输入框的值为 节点 + entry4.insert(0, d.Nodes[n][1]) + CLICK[2] = -1 + CLICK[2] += 1 + print(n) + +def element_reading(): + global List_Image + folder_path = 'K:/work/traveler/background/' + image_path = ['1_红.png', '1_绿.png', '1_蓝.png', '2_红.png', '2_绿.png', '2_蓝.png', '3_红.png', '3_绿.png', '3_蓝.png', + '4_红.png', '4_绿.png', '4_蓝.png', '5_红.png', '5_绿.png', '5_蓝.png', '6_红.png', '6_绿.png', '6_蓝.png', + '7_红.png', '7_绿.png', '7_蓝.png', '8_红.png', '8_绿.png', '8_蓝.png', '9_红.png', '9_绿.png', '9_蓝.png', + '10_红.png', '10_绿.png', '10_蓝.png', '删除节点.png', '减少节点.png', '增加节点.png', '线路增加.png', + '线路修改.png', '线路删除.png', '撤销.png', '清除路径.png', '确认.png', '编组9.png', '背景.png', + '矩形.png', '判断路径是否为贪心路径.png', '判断路径是否为最短路径.png', '矩形2.png', '完全图.png', '可达判断.png'] + for path in image_path[:30]: # 1-10球加载 + List_Image.append(element(folder_path+path, 50, 50)) + for path in image_path[30:36]: # 节点操作与连线操作功能按钮 + List_Image.append(element(folder_path + path, 280, 50)) + List_Image.append(element(folder_path + image_path[36], 140, 50)) # 撤销 + List_Image.append(element(folder_path + image_path[37], 140, 50)) # 清除路径 + List_Image.append(element(folder_path + image_path[38], 120, 50)) # 确认 + List_Image.append(element(folder_path + image_path[39], 380, 100)) # 编组_9 + List_Image.append(element(folder_path + image_path[40], 900, 900)) # 背景 + List_Image.append(element(folder_path + image_path[41], 380, 140)) # 小矩形 + List_Image.append(element(folder_path + image_path[42], 300, 50)) # 判断路径是否为贪心路径 + List_Image.append(element(folder_path + image_path[43], 300, 50)) # 判断路径是否为最短路径 + List_Image.append(element(folder_path + image_path[44], 380, 530)) # 大矩形 + List_Image.append(element(folder_path + image_path[45], 140, 50)) # 完全图 + List_Image.append(element(folder_path + image_path[46], 140, 50)) # 可达判断 + +def element(path, width, height): + # 加载图元对应的图片文件 + img = Image.open(path) + # 使用resize方法调整图片 + img = img.resize((width, height)) + # 把Image对象转换成PhotoImage对象 + img = ImageTk.PhotoImage(img) + # 保存图片的引用,防止被垃圾回收 + root.img = img + return img + +def window(event): + global MARK_NODE + MARK_NODE = 0 + for w in frame_left.winfo_children(): + w.destroy() + # 清除右侧组件 + left_window() + Label_1 = tk.Label(frame_left, text="遍历节点数目", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_1.place(x=50, y=330, width=180, height=40) + Label_2 = tk.Label(frame_left, text="求解花费时间", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_2.place(x=50, y=450, width=180, height=40) + Label_3 = tk.Label(frame_left, text="最短路径", bg='white', fg=BLACK, font=('微软雅黑', 16)) + Label_3.place(x=50, y=570, width=140, height=40) + if event == 2: + lab1 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + lab2 = tk.Label(frame_left, text=f"{0}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + lab3 = tk.Label(frame_left, text=f"{1}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + else: + lab1 = tk.Label(frame_left, text=f"{s.way_sum}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab1.place(x=80, y=370, width=300, height=60) + t = round(s.end - s.start, 4) + lab2 = tk.Label(frame_left, text=f"{t}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab2.place(x=80, y=490, width=300, height=60) + g_way = '' + if len(s.ans) > 0: + for a in s.ans: + g_way += d.Nodes[a][1] + g_way += "->" + g_way = g_way[:-2] + else: + g_way='1' + lab3 = tk.Label(frame_left, text=f"{g_way}", bg='#F8F8FF', fg=BLACK, font=('微软雅黑', 12), wraplength=200, + justify='left') + lab3.place(x=80, y=610, width=300, height=140) + +def left_window(): + background_button = tk.Label(frame_left, image=List_Image[39], bg='#FFFAF0') # 编组 + background_button.place(x=50, y=20, width=380, height=100) + background_button.bind("", window) + # # 创建 Label 组件来显示背景图片 + background_on = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[41]) # 小矩形背景 + background_on.place(x=50, y=140, width=380, height=140) + + if MARK_NODE == 1: + background_color = [BLACK, '#DCDCDC', '#DCDCDC'] + elif MARK_NODE == 5: + background_color = ['#DCDCDC', BLACK, '#DCDCDC'] + elif MARK_NODE == 2: + background_color = ['#DCDCDC', '#DCDCDC', BLACK] + else: + background_color = ['#DCDCDC', '#DCDCDC', '#DCDCDC'] + Label_button1 = tk.Label(frame_left, text="节点操作", bg='white', fg=background_color[0], font=('微软雅黑', 15)) + Label_button1.place(x=60, y=180, width=80, height=50) + Label_button1.bind("", N.node_mark_display) + Label_button2 = tk.Label(frame_left, text="连接操作", bg='white', fg=background_color[1], font=('微软雅黑', 15)) + Label_button2.place(x=200, y=180, width=80, height=50) + Label_button2.bind("", W.way_operate) + Label_button3 = tk.Label(frame_left, text="路径操作", bg='white', fg=background_color[2], font=('微软雅黑', 15)) + Label_button3.place(x=340, y=180, width=80, height=50) + Label_button3.bind("", W.way_route_markings) + + background_below = tk.Label(frame_left, bg='#FFFAF0', image=List_Image[44]) # 大矩形背景 + background_below.place(x=50, y=300, width=380, height=530) + +if __name__ == '__main__': + root = Tk() # 设置主界面 + root.title("旅行商问题") # 设置标题 + root.geometry("1400x900") # 设置大小 + + root.resizable(0, 0) # 设置不能调整显示边框 + element_reading() + frame_top = tk.Frame(root, bg='white', width=1400, height=50, highlightthickness=0) + frame_top.place(x=0, y=0) # 绘制信息输出栏 + + frame_left = tk.Frame(root, bg='#FFFAF0', highlightthickness=0, width=500, height=850) # 左边栏 + frame_left.place(x=0, y=50) # 左边栏 + + cv = tk.Canvas(root, bg='#FFFAF0', bd=0, relief="flat", width=900, height=850, highlightthickness=0) + cv.place(x=500, y=50) # 放置绘图Canvas画布 + + cv.create_image(0, 0, image=List_Image[40], anchor=tk.NW) # 背景 + + G = Graph() + mode = tk.IntVar(value=1) + main_menu = tk.Menu(root) + root.config(menu=main_menu) + cv.bind('', left1) + E = Edge() + W = Ways() + s = Solve() + N = Node() + auto_opera_mu(main_menu) + window(2) + G.lab_show(frame_top, 0, 0, 0) # 显示边信息 + root.mainloop() diff --git a/X5.py b/X5.py new file mode 100644 index 0000000..de1c6cb --- /dev/null +++ b/X5.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/30 15:49 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : X5.py diff --git a/background/10_红.png b/background/10_红.png new file mode 100644 index 0000000000000000000000000000000000000000..fb37986cf7af09684a890d57e79e48acb3295e8d GIT binary patch literal 4705 zcmV-n5}xgeP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91GN1zh1ONa40RR91G5`Po0Cmf+s{jBI%Sl8*RA>dQnro0{S5?RNdE9%u zr+a$3dor0x029dr)DR4q2V^jST8dRZzWPB7KFYGn601T?ND+%N7?KboM9VLr#1|-K zX_Z!4XsLz7(F_S?f~IPU2^k`J%uF(meoXg$opb#C*15OS$OI;Vot(S(+0XS~>%Z3C zXZK|3yOf^2>5~^NE)H)_Go9;-PO~p{(^AT_PElk<)1*|_O_f(^SdE&~%`iRNjOvH_ z#|QT=fBe1w``r*B`%We~@jHL=mf3Rn-n1kAa50zmWmT1B2;J2DjK?X<3d-gz<4Q%@ zE|-&0*W{h18Pw0FmFn~T!H(bGb;r98zEXK!5fA~)&DWny3+el_@hD|=ozkd6?L4aA zRMp7N^aTP7sw`5U-)9Qy^Noi1?1SRfB4#+ zyJ!9^-<7UI_&ilr5=b*b(1xifN1|ShCoyTF0uzwgB?C+dpXC6e?7L5=LyiUWqOMPk z{=I+f^o@)6-hAX-v79rI^*cUwSGljaDQm`M>h}>^3m_fZPGhuexv5%ODe#m|S}30^ zqu?fAEdyrDGDW8aR#K;hQaKtOukW6_?Z(?<%(wYDCm`z|`P0vpi}jCZXg&?m`b7BT zd!;qRK(2()t3mLXSO7dUBXs-%5PFrg<3Cy;1vY-{i&es6VJX0LijzUPh(NRiqAG5v~@;iqUddVbEHl z84Oc41b{#yuC_ozqfjfAYPF6gwXaO!130=p>=bb zI|w8&Euer}GSoQLCApEz@mRVsaHuGi0~3l)zGug4<{vt{v;q5@n^x}m%s&vTy(1U_>nm!Q(r~_E0;Q9kr+)7ggFlVEM z2qZB-K3Mdc*N=Yh(|@II0(%C0#!K^8U`3P)u5fqTSz~3=FUIA!&SE@ zM!cAg-Tc5MyDpzOlvU%h*~AL?LpZs#ib#c1kFb!Ih$u6m4O3)9A~&^6<=Q6C6~;0Y z$vTsAL?o9*CR5#X3Srrt%=moMG*p07FFMC*%b_WYT@ zU}GyAErG_HVaw0hrLjl`wWMtDUm37M>hK_Ngj|LG1}o5qezEZ>R8WmNxi3iTOeOE8 ziKPQyggca@<*~5Gp6E>=;q=y-+tYi15pU;-GXrNJlG3s=1@b1KV`UMn=Xry%v%l5HeUlFYIqAh4;efXi_ z9Z;_A7O<=Hwv{{o`q~h_m8LDu?VkT51m)NP2%{0vDFcKS;G~)|wBMkm*c0>tl^C0v zi*mH!of5($6f{$qaQYC8#wj)IqwtaQ<2YVjCqi|_+)~Zcf&gWP9W-qykTy(~)hX}I zHJ_m2N*-HNTD|?Rt}hmf*TsIJGslkI0x&`eA|^J1gt!cwMi*BHSS%XTP1>Y`>+7KQ zRkL9M`|%$Yz2856LG2w_N9DzDb*{hsa#)89X7t)P0~O@ zp9m;QX3`g=6;^7gZ6#K*T6Tb>obB8YwMTCK+lw!_ynHfk^s{DzFuOU~0Nn(`;RRNu zTwrQP{zq8lwa38@r>T2@xa#$3{%3B$MT)fk4-cl^p~ESgQM(Ms(T5kkCe6Qnf2(y@ zCN(JS%p?DmdZ$jL0zi$S-G_ZkzYJx%hq<&FRRVD@GB-FhW!bDf)m&8WUMk*?x`0W8 z;ZK1b0L`N8{y|$q9N?D*B_qS^H53{Bz{RQDe`Q*@^wm_hskXnoH-|363olIb?|d7| z%~JolV`==t$yDw=Kh2!KJI!75rnK?MV`=8(^UN=`nOx2o5Tta@n);|h1*KI$zz9)j z$#yPfH|2RZT_>#~YDgVHj1eH{ML8msAfywIxzU80(cbgY&KrL+%>rU@^kk|%NJesc z`{EBMp!?=G28fNrUrX76gK6gJucz$MN7LprN29)T`6U?Lg&C)=$YYoHUB%fH>%W*Q zYSbG|i~OxQaeE*3X)6^JM-LDVR3jmf;nhhMuI-cIUTSc3axJZX_Va1@2Yy?LcnO~Uay<#D>U?-5r$<+>FP609r<@l5hAoNp6WG-J=Pvg5kn|hBPVJ^mmHl3P2 zB=?=PdpXLZ6Dz5+i4WQb{x^J}84yOR>(SmUETmdmAxBUWRs;yGVV!e^Oyov2l{^VZ zd7dxkm=I~HoB)Y@S`K>ACU^B2>jz5GY9nXig2{6ywQVa=#%JR;WqwE0R%>Ud;D3%k zae6SUuMbkSzR`{%a>Z#o{FL$G59F?^0%LyHpv;CX8(LY(ilSR$XIe}OC->1-FvJNQ zxINit0z_?$csGCq-K=j@oQgC(Rry>Cq~-vct5Nr<%uowb8DMlIWi!3jQm#1#BUk~% zyYi5HmumFO-0!dsr)PH{JAfq|icWUqBEWii84-wb6d{u2S?FnVUthTF*)qg7+O<9s|O`7Ut(cS*2^Z)l~dJ4$I}U?brE&jXSbwrUmc zrve}uA~=U)x?!2N_&Po9N%q65!#ar~glejYw!u~?o? zp1$$30H{`7!|`sC)6ZeXG<8;$@R;S4o=(9s!7zzY#xV?bySVn60CsVPd%Eo`a~dEuYg{p@X&2HGCjG>6SQVLUQ>qC6YJ*1? z=PEXDM#XCTea#?*%fw`~u`_^bR%|VSW?U%zi1v{-YR5##o-R&p&=X;E`yW2aIBuyH zcBIh~A-0bo^lkce8Z9oSa_0_oh6RrtYkkOQZsiz$ppU%0R zI36ZbaCYlB^r?Bt5p-itMuPVgjN+^}GV&Wg+gVG9mRikt; zKfTtulht4k0C?JOlZ583Yb>OxsZ)EQO{O)uuVm68_?@NR3ma+k z6Az}r*G~lSnOE;d=q%nhA2^V@N6$q6W;pRJeJ=nZlX)mrkOES8>f$tl0{QOkpB{XK zmXy}+KX9BA!=7dX_-BTx*+haq5AW6F6k7YuuA`}Voc!xd2J9bavx2DWt{ec`9BVOVfzHUUbmM4>CW*oUw%;N&zjm zQ=&h|$3PcqKh|ad4xoD2Jl^}QYp$SoYY_{@`sf$a&iQ|XkDRd?Z1{7p=qNlohF&$h ztI?@FW=lEhS!hgMVfe;W72^b>;BkY<9EQ^zj)#Eq9_eGZrX<+bH7K=up_ww{+mpDJ z08wdMml6?x>cQIhSCrl4tXpa2qhEZux3l|0VU-O+Z(Leqa>_N1IrcoH>1BbMH1jhU z^@q?(!sDb`AzszIibj@5R2mFyGiijaVT>>1BhpMq5fGYcIZO^n0VzX_U~&F} zi?iEiCG7~=hH%TSJP|g~3bM7FVD**Q^(78qB2p1lAi{X<)X8@{dYYzsiud#HddCxk z=d1f8A{Z42m2vSr(~XPgoa>(U2EE;H+;dtG?Vcj#JS7_fJOQW!eYgUQ!JuZ-tJw=0 z_8?i^uouK>!4reWBF1@Y@I=uNJ>91=Z&(aM))~6F$QZ5H_wK#xN1qI#CqJQ+?G=_! z-}9w^omrZBN5bJf6Y21=w{U#-2wrg^(GW8E+j$#hCKH5T2*i^r>W)vH33nm{PwM4Q z8zAh4MIsnb#+hSZ!m80*~iJJJRiSFXr#y zToDnyHUZOz9{>n2`U~oAeWnFvL~HrNw6sYx13-?G3OY{D!KapjbP!X}Qvd;wKoJx| z_Ka($*gJZ(QLd`-;n|ztaJfVNfBFU=+1_~Sxz#uA{+{J0%U-blh5;94B~v(kQ31YY~7DZcg-{ z8DFzq#_i=$$o7iu<)?0aaOu2#yT4S-l;7*OdVJY=4A)A2Tkv5doyLIh`e_U>^Prp) zQC8M)*sz4}2D*v+qVJ!z_DP z+yk&{XI;MR*Ej!h?zSs_Vk*l^<%~MFbn3%jzOT2u<2Uje!n@;hd}+MS^xN9d3J`bx z={I^yGr-6zk_wLmzzD6-iGOg@f)I#DBK;)(a0ETO^?10}zi;8TD{q;M{Fd1{196tq zA9&=Fd~yCW-MRA3Y$)7?BBhsdSSBv#v;r8z4YUTdqlyGToFNRsunhirDK5CMj#iOY z0_kS;cyn}cef8AO@BPsAUyb4C@bj{OOy^s@>(Tw$O!tq=+3scW*DQB?DJHd~n9>U( z3J8lIOf95y7x+XC&}pc32XC;>xEdWgv!4Ib-Vc1=e@xdp*ZdU$nNEKE-beSBS@yne zw|q<4DfVC`{;3Mu21sq>!VX5f)-GDX<#2AYutc8o4~NFbN8|B<;YNMm-rv6Jz;y1H zn}0_@wl8+z3C-p}j?sFF>Lay713^08tYP8VK0bs~iZAvcvyp|HS6q j`#yB-k?r%m((?ZS!s#RT-ku#K00000NkvXXu0mjftC$0( literal 0 HcmV?d00001 diff --git a/background/10_绿.png b/background/10_绿.png new file mode 100644 index 0000000000000000000000000000000000000000..666e2eaf58033302260214f224c1f7d2d0b60b03 GIT binary patch literal 4865 zcmV+c6aMUpP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91GN1zh1ONa40RR91G5`Po0Cmf+s{jBJYe_^wRA>dQnR%>T)pf@A8ScGr zu-~%{27@4h*bXrXQydf9Kxko`XNduI5unh(e&wkS#>*B&bu#$8Zb1+KKGon_da`n-&)^V zd!PNX^iw)6{N~29`b#@E)FXrU)~)X8S*xC`ihirk>N3w#%ImzJ4N|WdWQX!@xwjbP zJNr{}H+}!-zyH)v0|G<5`QxI0-Mp!{BLA(r(|ccUtaoZ%*IAb8lxKAs460NVP4||0 zH$wr;$4^w-_-?z%zMfb0(j z5J-Tl0a>NG0yu;OZ;O0^i~tMDvWyZcGRXQbhgfm6^`rcm;#0e>d*g)O5RrG?zIk

uvisitpvncJ3Z}@5s+elu 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 literal 0 HcmV?d00001 diff --git a/background/10_蓝.png b/background/10_蓝.png new file mode 100644 index 0000000000000000000000000000000000000000..6b97d97dd96224d2c2a504fa80588bd35ddd1123 GIT binary patch literal 4382 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 literal 0 HcmV?d00001 diff --git a/background/1_绿.png b/background/1_绿.png new file mode 100644 index 0000000000000000000000000000000000000000..1c00032bb39e9d7ab781154d396a4f8605b43918 GIT binary patch literal 4653 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 literal 0 HcmV?d00001 diff --git a/background/1_蓝.png b/background/1_蓝.png new file mode 100644 index 0000000000000000000000000000000000000000..e51067b86ed2a1589feeba7abe8fc582d148c797 GIT binary patch literal 4162 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%0DD#mafb1|6g-49VamXs>i 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 literal 0 HcmV?d00001 diff --git a/background/3_蓝.png b/background/3_蓝.png new file mode 100644 index 0000000000000000000000000000000000000000..c75e417112a5db6f10ea844c562c8dbb1e796541 GIT binary patch literal 4399 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

5vqB z8L0sdt}-@09x0PN)wec#6-i$ycxw{H8(D3X=&;rsaA;z*e$$VP-lan6`fICj`&*Lm zs_i=62g*`>9qx-_*$yupOv5t=IIcr@*;T|LpyHvMa~RiN!5h@k3s@VF<@9*1y5Hd@ zUGnB2U05t;QH+qp>QIUCZV$O%w_F2#0H!gufosW+zGedqJ5zo zXNk)Nw&e1u_v%#=Qt9c+(u7aGp!IQk16priPq4hYd9Yhk2N!G6M?ee)#i?{*Aw3)c zNe}^mGFcxm&h*h-37CbKn2guBYmp3E>dWZY%x`{E7eo;oU)>77aCH033zEs=r$#=$WkixVO`3hLuq*EpBKWLZy64+yJAhAsD;Mwo6EQ>Jw~EOhWWT{ntlFDC={C*i55^Dv`Kb>)@1;SQ(v4k6g< zmEpmES_uF65VvDegS??Vgb^?r>w0;$4OnZE(!uRa#rl$??I=-QzeZMvzTJB(ec^+@ z(?2>onyvvXR|X$UvJ5%hX_0*8fQbphED6)6 zsxUnb;g-O1_mVdR1gkR};CP2RU_GYBQe9r+6sI3qh~v4r)5YOG{7`p;njxGzkw1a} z#lLn)(#)BFa*YudDAI_O@rgox%1!)68{3TQ(S$@_7LsHr96#0$`}ViP$rHM^tKAaX zHe@ghu2#WtBn$YnmWzfO(1_97=#Si=x%TjMx0g_!tUMeM~)sVzl9KBTe^MWiRBhSnfvudA&Z!xy@I?zQfI#A@*qQT zUDj?u;0SaxpZ$+@ApZFq1q#w)aqgMNup zhcI~D4-;*~qsRMqs^&tM3tl;Lw0sOflmZ4aeDSe@S_%EgSUH5$<}0hu5yc9!#J=Uw z0|;aQ!lPkG;X(GDy~2DEa9C^4(I*kfMSaNAVZ%~anL3{DX}m!jy-Q{4czK&HcLi27 zcmZt`7`&w8TVA2A$3P_C){WPPXFb=Uh<3c8Hze#u)gvhlXg?U2h?77Xp!BhiIAE

v}4E2M^}EcJE%dZyEGxkmViJ-}mnRe_Z;K z^woUhiYBbGk`Bl zW@P{9NIC{OPHhT#p6}WI5~MZ@Fp>+1iKt$Rl>dOEI-$WnaRDImfTpexa$S9Rgg(D( z5+-8#zz=-W&+hGAS8Mu?m!_V-+@-%GPHY=}49_@Z@PN=dm8 zi$5F1cJpaMjMpUC>=cI+eRV%pIyF=5JFs`*6`#78KZaUs{qcV7nakKS@(RB`;uZ1@ z-vl8AP~#``RB1vEFfkmFjzHP07FLtAO)q4ZCyp~-(Z)Vpety?Sy02Z1=Unxu{ahn2 z>U;Ojg)HmQa`}`Es-~bV6jYdvv(lT zbn|B0Ic1|<#T#6_4^I!ic67G?^Sl3Q?5q?UrOy4?=sox7d3t~S&iNZxO|<@I&1Cvg zemS9-6d_h81t^(-$Z;=fQZKS`!y^&vc!8GgPEVic&Hd8H{$k=$dcK!(QAC;r-utWb zH>TaPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91GN1zh1ONa40RR91G5`Po0Cmf+s{jBIKS@MERA>dQnrn<**Hy>&dE9$v zJmc56j!mM_v`tb#ajQZeZXMEwhbSNr9}p5kl$3|~f+$jIl7ftH3> z3leWoRgj2);t)5jR0>5AeS{Y3Vn?p+@pwGS zd!LyieS^}mJKy)x9Xlom)52hXHR$%HVcMC>Vo+5@)paSgZP%1dnl#hybT>)Ix@r5w z#)U}gQBgK$8CR<6 zyj)2}T~`jeZrmPAXPST97;pK_?f1Xw=!=!-MFA1O(sKL0v@LyGF`K5MZBv>ysGUdk zo0=BcnZ80`LDf}CWl1yj0;+g7DM4MQj=Jtl`|lg8_0Mj<@25T%Z7<-*3j%Wd?%#U( z)?EvKP;O8A5xz`KlLXRD5wv3}%8{s7<4H`KsKNwfb}0Z8!WSigDEsa+7?5MZvTEB? z(|_4Ge)^6bhYvn|xmYe6$lCoMI8^Vg?ku`loi;WQS_>c@+DV+KvcyI_fTMc2F(hmL)O56uf%Gl z4o@E~W|I)_(l?Q+R%yY@=Y?n*{;+3P%Rn>GQtT4`& z=*E*&OaLH|h^rY$XcTItTCKLxr1q65d;ljGTr@zPsEd_*P+BJxz<2|LrV^Zi0kF-u zeP;a&=dXJ%F!a218ohMzWL3>pIxG>-#yF*QEU^I~Bh&=Jd~|HFsBY;Zweadf!9wfi zG!GC+U|K){wPdJqu1j(wnd7l^XW&p#DhDQ1gL3zltCyd+w6p>HtDDX|^r646mWyiz zkhH`G01&zZKrv(TDUX5>VpEF%WU(lc2_X2dd_fNi(rNxg5TFiVg@Wr3SaCDW6~LUG z79xb_nbS_%pf1TIF>hG&@R(*eXWg~35!_7|wi>1h~X*i?gdDsNl*p>q%X;r*_9 zabm=Ubo}6>uh@S5!c#>vtGn}90e=W5msSy}aOx=*(h?D6dfG5WK_qfhD^#v+@?2po zLy=<8mm?y%EJ7)`DdwZU7NEMbUdpPV@EzyR%1HIlb*AS&vwr=~LvQ|Xhnr}g2v4tuxiU zn|ezJz6f_HN6TYjk3G?wK*H&5u(W&hC?np$6Uo^k9l!U{1H+Z-CLPTXt3QNO^P2qv zZ82$tMwO)w^2}_8y>rj06%91%FGigK4nFLyr(Zy^h(=n+uxuae^%cQNFWQ1u)`uS& z-T~#>ZUNf|-+AVNKfNV{&(h@L(yrzABPhoXKp2gPP6Z&e04LQ{p#2Ul#h#!KsKnUR zT$G~)@01W8p`e+Xory=*+QWX8=YhLBzyHkPw$)^XTI00E8lcAGL^}E0>YolOIU(j-v~|uPVLD5utv^f(joQMX>#~q*iR6G zBbx%ec*Ct}`RXgF=lyR#oGL635?K)zCe*tGPr*FiTf!m{hV4YMofLF*J($uBimOik z%3oex@7h_t4Ra}N2B9UGLz;zE+&^gRhy(njfqDhT>{(#S;iZfZp4(U1GPs7k*>}Sx z2zz%3R}8iUonlIlB+TY~p-eerK#Q0(1*t73114Q6|fg7 z{lT@J$BaC7`IuLQTIc!~^5|~8jH$}km1uD<7lhndL2>i|;XpN_KTys>=dyhY+$%I_ zKV5AOD*VSa%QRlvlD2&Jfz)z`Is4I%D@9K9&F3*KfW@V><9iUXv&XyZt9FD{XzSwa zWnb%En{r)VS(c+}TTEalI>*V?4q;9KY8@7~J%lEwJ@yygB}8blBWtCSJlH>c7kxgMxxL4~%1VyvwK z=U@b@u<@=uB;Tc)qulSX4yR|m)(&84<%I}&_z*yWNm-EhSP<{xyauc2KVg9d@zXa< z?;P}p9+SN*Y59#er`g%_srm5HG-9Dpj9TtfSYd`WJb(4F#<56&U0H_q(_O@gnxxEF1vh5%L&Rx2HsS>5wu5$CijmPa0GM*!*qAPxFP=$n4?>o6d? zwrFaO$8=MAIt9xFqo&X>bGjdN?Do!&L*+#R3jM?RXlH}g%-zC58mugb;LAUHAiy(> zMi{6NeOma5AB@uY&mK!dDCRn`fVoPUgEnGW4!~;O4k`_CWs#3A&ZpSAn^vo=u@E5- zFa?-mSG6CL@XeNjN#q$=yiSpBq;LYt3Kdx6g^ZNXU{SpAK7aMdVcBpm?>v^zQ&@%8 zs6#n&DjePpy)q0iQm_v|OmVvA>%@s_sk|aka>3~;rF9t=gKl|GVIWmrZZKm!mB*4; zRG=On*{IJ<)8xLt!tB&ZJ9lRo!ra3M9M;g!6#g-HcRc3%d zLwsuwF4t(eZfmgcn70+a;LbwdK2?mXrG*>ujCJYLahncdjf62T{V`Uu-gOmRKejGr z>+2{t({!{veRl8w=O$#O6HmSyXJLbGOkmz%VuPHAVV_;>*@7j;de)?y7as$-+KzV0fQ!aL0zymr- zD{Gm6Vrl3!9CIRVh$w<7wGdSt4Preplme3i8Lkt=yRokTl7{m?D%PI_Tqt%!`<7HIw3>9U4GJ)AHSH^8n$M_5#Cn_S3v2mJt|!P(%d5 zIw22}h`V2?#Sa1qeapES$U|Hy7AfAcwS4#Uk8RT0vPrNJ{_QJ#mKLCMYSIxh>ciA^ z0GfSe@(7K29SdvkIr?AqLU~;$rOpxCxo8EwHrPYhm>RU?exmkUbN-cTS5OgI&^+&@ zl;Xea2{F*3REytYMLj!bl{wMgzv6aQqa73kn!lPs8)v}Qoj`J~F%2AK-GwLeCBBt`JC7AdQ zMO#4x(t%4je&MLX9KDqUn_Yua)(g^8X0Ch^w-O*KZS$i<1fX_&cJ_11ZtB&NU(USy zV^5B@4sQ&rtP@e-%*W51`|-i{;Zr5Qi`7Sp z;Rr!d=Sbm7{9%R<8o}JOdO&C@bC?{E0#b$;!Q%V|7iYK4O4kyJXC{7eaPa{@* zX)y>95IW@X#5`SV5AQkjJ)gfIx$3w?I{nZm9$VPCaC>}N!Pyf}>@6JMJ%TrUkmv}R zaSoGeeG0!2NF2(rVSo6v*$_OhchwvqGy)chU_hCm5&M$9Wq+}G6k4PpwaA;v>?4bJ zz54t6B{#E60F z_Fdn+^7(pH?J#;EQKl8Z^4EC6GGzCj?R~!R)@S%c8UYr5{wLCT5knQ^JkldP$g+Yn zFaNbpMAx7Y75b!6PG{F503qC*-1yS$=8I*#xU7WFnZ9uMM|Zwt@2*c&3-!yqtH;f5 z17Wq2XCHryg{EhK@We3#nEB?>pR%%s!-ge%M>sb?l9Z}^*9sz0t=<|bN~uCiaMzhN zv(JwIcYM>%_q^t`<1UrHCXo4n)x)3uhvBx-cO{%Q=!Fx_3 z@{45);obc`w)-nNe+6M^1&BMp_jXgJ8F_vU>f{FGR%qzNAKYXR0wGA|y@Imgdf3h; zXEz?+cF(KsigC~LgHe~CPQU%*uPApce`vT=e+L^1pAnG~T6vGo-=xf$4{K-^X=hRY zayL+35qV$t1D$M&fS^^Rl@32F+S&Q((Y4i6Ke^`}2R`TM%SkT?$b7=pL!Z30SQ!3p zy*Rusew$?omSR#%iYdJyqJXgY!Q4VRcZE;10G)^0umIMXHPffgt(8Bs=k4G6_|n&pcF_thhjWvKCCZXN9Gab& z&Spm@>+QpPe)Tm+=5xQ${Oba8@nXl{`Kgx=ht1oHVSNh}+EZ2KHb}&c)<5?FL`x{> zAYj+7av=OacKCnYIC=iTz3;f?>5J!ivE}~Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91GN1zh1ONa40RR91G5`Po0Cmf+s{jBI-$_J4RA>dQnR~2dXIaL-b=iBL z+njS|hUqO0Ee->r?Lf&e?c7R9Lu;swR9hPr5e5G;D2S=ikQfP&5-Jf%j4?=vF;y-R zq6UO1Q`70RbXp9WYOn;RvCwv$>CBnV%(?Bo*INF5@7nuxs?&C+vd&p&t@T}>=Y5~| z{l2wMmVQFV)nDCl`EXtD&Ss*zw(0aQ$vVwkT?{)-)|7dcQr_gltV)BT%1-3{@|mK_ zA0D1ux%<06^WFdYNkCwVSAM+tE4y|MHsl{{dV{wPrUn-_O_OD*NqN?!s;W~_w8LBG zk>}&NvXs%1+NQHy9!`t-{hgEfpC0_#gAb2eobUA&5!wFbox28?roZe@_uo*}Ey!V2 zK_CIH0c4$;8sHETye;w(837iQWf>(@WRUeIhgfmC^B?(-ir+YR+xZhZFCtfceb?sB zZ2IfrT>si(Rp$cH@)>|djz@qAc>abLB*F}NZh01MI{3Z}@5x;WW= zXz*JaTTSc ztg)?uV~;|YQ55tk$R(5)DX$2yh5VBby!AUDw%5xYG4#td+O~h^H|ve7H(~r#4O1Ep zY5;OTpoIgWfD95$tcNVOOv$wO)G?cqDnfEa5NYGaHWGjorw$~-f_QhK^H2NteCOtu z!}yX!wmz_R{Zx1Qk-^mPwH1R`!zv9SQq|Bf2n6djydY43TRI|Rj2I6M^;+KY(r13O zyi`le17$0(+}NRfe8p#}yHY$}El=(|aNU807bEndL?kgiJ^h1WZ@6JNBnxp4VKYEh zL4w)y3|3=8PEURno)W4d8$qpC0B@G^na7lu_AR8WAHq{Pjp~{w8hM`@J3)>!ytGn0 zTR%1Vx&ybIos2`D^_ZEQe6;RX8`2PgKtgL)x84x1s<>d~Mbqnrcph-2c;UtYg9+J{i6-2Q*02%_w`dq0A8V?CDqM;rJc>*i~%{VFnUUo9bg$u(N zr5u0n4{~>bS{%by2Z>VofBq9kk0v|~WxA+^toU9OV zOd^?f9T9ZY5ifeC1<`t_3ux19j_C^|F^6|TQ{!0M)(KdvtND-+^*$F263@(#V{L=D zF~|!Dlvsaa6f6e!L^tI<1B4zTBG_H=4SG|!GF4jbTvT_mvNPBHtCm9hca6x7`>x(y z&K27;tf1ySHF8ryf$OmEv!Ln7z}iT_W+!#r)=|k;#d;+f#?;b)EdU#fpPuPS3__l` zL7C-}qpq0fM0TH+9~-wDVbtui|6`jvafi-kr^6uh+89pO6*CW(~$;tl(I1xLgr1kGaN%SNg& zj=L9Tzs+>5w2G9{~D;rsiW5S*WchM#S<%Gm3V+`84{Rl5IPSyiZ z)=4vi_34Vv)v43%rSBDwrJ`)oRBtA%JKdQ|%Y&t~l9tn;>1*%kj<#ust(7P;mxOaQ zvFZolluee`3#Yv1AKv(yVlv<4Mo`c#0W1mYh8;*jrwC|KVtiAo*RgxeDlTX>o3QW* z5?po(ezq0Vow7xqFX{`fMN5c?W*lDV2C9cYs)%F0#-KpY5H%Bl5d#xCsEZ!ej1o zQPQX_eU%bcG(O(D@dIll#$(K78`1^E1u^FF`WW2l!-BbrXNG1vJW*GWAa2-JNEHZr zm-%AK_8Apr>*i+fM6%fqaE+{?tQb3h(~zrY1Q-Jt$Wb?LuM9c0P6Bl3nY!>8UP;3; z?V5Ufde_D8ON+zP0op&a`8>nWmvc6o=Xs7&V1`G4ELe;al5$RNBL*QDh8#|2BTB;V) zXP&s*f#M*tu&gsR>3DNAooE)qyWwHR2-!H2tX6g4 zkd^6=ElEI)9ET?&*ogxN&|1{G?vWTnuS5Yw?YoLQSk2L?rqai*{IfJu&Za+n{CCnq z_57>E(qJ*2h_RGs4v}MNac~M*Er+q(b=8A`IWAPNX1G4M@(wYVvl1{jBvcUbHOFgn z?v6VOqXU&eD%KC~2{>6RKP|x~RzuRB6H;DD@zI|e8er)`nLf1n<7vz673m94el8t2 z@eQ52b}aW7(^CIbdV2NA@QC-z{bkA)*&O%j&wvu;$HDbfsD!HvvPNvnoX@oH%ES_C z2eZ6>FvzVGtSQ&8BMupBjNXta&l0n#3!DpL%~qz%=1#Bu+-2`hH(&M(>EDh$l0JLr zu9QvNMhmou0bTTwDq573^REt<(xBgxaGR7HbI3X7a=Ro9Cgd|225j%$#7(h`@{#nBe|>-A z8v>wN6p_XF=&!7#L0U?aQxn*$m36=1^r5%2#DVJPSXV3Qx~ zjQrA+R=P=a!5ad~X(bjP-~^5ZLP87_D=X=&@imx+u}Ct}x?)qhXy#J-ROzF;|9oUU z`f?Pmc%$PBTkjP`djI$DNGGx*kb!_%k$NRmLr8}g7^GQcGl@6g#(2_EGqePcJ={H4 z!)#F2tJSG;qBBvu9iX?{lhQp7%_s`@RR*eVmOXV&IF^(u)?vOp3FvhHq*Efd=Yjjo zhd0XGqx40ILHu^%SIK%5hw=8vJT9cE>c?WJBK-Ifu>igRA6`%?65<@D=Sp5t59e8T zQ?Ms!TeeI9uyCQo^d0&)I7Mw>OI8`GapNh+Lq}H+r<=ZVWenc&O%XfBnk^k5Nm7C|ThTUN;51I3Z4b%BPev8i4B!TdogfhSxDHp+)A?Yf ze=q`^@yzKZjk;J*7aHgVXIoEa$V3~hi)-;066d53uLq2+NGUFPCB{364Q<1jKLLz@HM z-1PX6^f_R%D)g+Jm<%a~bld?jE8L-hTtZS1XRF?R+OeETH50!LeY-tNlrKiGBlkQB zhXc1_ReLm1heEh&eP6QB8*S}VfGa!vwYUHC#XhCwQ;QEeiBfVEk4d@L3niU=C2+;@ zOS;nY49~1t$=;q0r?$KT6RRo>C1)Tv#+a|ejOABF*BuiGw!&yKbY`~xdJ;KBOwi6B z-oj(d7@K-q`m=hf-cLo$2&?qRV_6wk#s&FUw?T$`ngtmG+}BY-#t(8@+wY8ICkS2- z`9timFA`j6nP-+)JWBsIkN{dj07$fN2^5fXWZvbCJ&YJDx#KyG{ip{`%%QCLTJ>or zqtEEL{eeB-pPHWB;s&yhmXsI&17RYvRFwJcx7`@}or{qe*xi6vvYneQuCdx89Y0Xm z&ubAc`g)hP7|B5P3u8MuL@d*$Tnn?IV6_Fnb$)Ip^M)R*-PGB)NUKZP_r7-XzALC` zJ>t#csg+;H8^$OT>H)GBgPLabOb5?&7+raFk%mLXCX*qwQTyZU-U-G%ktaIF>Y?Y5 zi|u`nrwyl8uCaFOWi8EU%$JewoTHVhp20)(Yv%%2R@O(44&O(EI9j8O9&X#W`;po8 zGu!=GFyQnfwVTKx9?&~&GEmQL;5S&V%|@&lc)=jox-PC7!h9P4K47EP{@1F78&!by zI!>w44ifRTB~U3XJ8%oqn$Q_BU_+)~*qBTAgt$oLu=OOj*T8GXUvA zZEwafW(;^I?{Mw$;t|Qf4{LZ$_qVO-$GzFn7;*9R*+s?Y_;-R@k9cXlqDN{hE@&;z z?hnR-Z~g2wQorO)^Hnx{_P^_Qp8*}5XBJhPt;$L(AOqFdb%n0Q+PYB0zfKbd3hg1x%y+^D=Fm~-?%7Sc&^!d-|hGQ^a$$OWo@3d zlF`jC-+$eq>8a`0xEUO0E;E@iBY_~q0_i4GGV!q+Lbi_$q#h(oMdpH;Wo9(>{=#EK z3NMDRXru?@C-j0Bf>@qTFV#Qz#;tp|ys&@!q*%{-Jo)1j+cv+td#E?jo%dTU_6CT1 zA1uzI=CcOGUlA)L?ASR_&4E8$>;-=Y32R&c1FBx($3YBqEgIjhj0@(AlDLN+ZTqXu z@u!|%y2dficG&+#kFED?UH_&_*8j`&^c0>ULxEDuSL6HegdR|Gf;5&~Z+vd!3r@&` z%8-cnW;kunRWAUNf|0!N7>UHgI15Q(2t4t_C!bo{{@8Dx{r`uye@P-X@c6~OJNM1c zufIW=C?x?0A@T7uzL%(zyB)LK*zscl*aLh!8WiH$C+r@sXyYMD@QxDHQwIF86RY>` zz3twctb3{BoQOEUt^c^~Q=2wj`~hYzw0;CR`qA>f?~gs=zpjG-Xa%gP#-zr;Ej9?zNCfJ6 z(7GXFKQ{%a5YFq)pjpYtLz z9`7B0|CZg;vl~A@IXkySY;NzvtOBRe^Aht3c%vkt?!!Y7Ka$b|dIlH}S~|7-_^H)X z@BQ|z_dOJq=kezi5gCtvBZP|H{GF4NGrP)}saNr-Gz(5)dHup6N1oSSQ!TRI;6PRV z)5}L!mX{t}sTV)>@H-!Pa6I<;UjJW2&YWz|J==e(w{i9l%Ffh|veUh=C@0n-{C58m zWKbI$GxlvK2mRH<)ymSr<%Q+D9{JV%PnU&+800000NkvXXu0mjf D{gEYo literal 0 HcmV?d00001 diff --git a/background/4_蓝.png b/background/4_蓝.png new file mode 100644 index 0000000000000000000000000000000000000000..0190f5d174cac8fdc16c0d085603d1fed41e570b GIT binary patch literal 4228 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 literal 0 HcmV?d00001 diff --git a/background/5_绿.png b/background/5_绿.png new file mode 100644 index 0000000000000000000000000000000000000000..3c9976b7f4412d1cdde4005ba42fbf0e7f72fb25 GIT binary patch literal 4857 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 RifaG>002ovPDHLkV1k_U8tni8 literal 0 HcmV?d00001 diff --git a/background/6_绿.png b/background/6_绿.png new file mode 100644 index 0000000000000000000000000000000000000000..669ac3637942a417af087a1abcf4b9529050edbb GIT binary patch literal 4908 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 Ze*_Amed002ovPDHLkV1n>IBHjQ1 literal 0 HcmV?d00001 diff --git a/background/7_蓝.png b/background/7_蓝.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc793c9771f0e9c5c760defdc3b0bc369eea5c2 GIT binary patch literal 4274 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 Q(EtDd07*qoM6N<$g672(y#N3J literal 0 HcmV?d00001 diff --git a/background/8_绿.png b/background/8_绿.png new file mode 100644 index 0000000000000000000000000000000000000000..3fedf37c59c60237a9577fa096ecc55d00598d90 GIT binary patch literal 4908 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 literal 0 HcmV?d00001 diff --git a/background/判断路径是否为最短路径.png b/background/判断路径是否为最短路径.png new file mode 100644 index 0000000000000000000000000000000000000000..7f404f4effd7215c3b4f9898617a939149889031 GIT binary patch literal 3431 zcma) z*PN9(E$7=Yr@Mx7`sttV-@nK8cwMjSy586O|K}BV<2r^5ehLl%0GF|mp~aC;9q9~= z{V1nesTmv@h+u)y2PnNFOGk~No2~IZGc!Q;sD=Tc7!QE$@5+%*9SHy+c_09C=-FkG-+~ z`Q<>2X8?A@54TzWvmV@fLj@@PI0=?o-$EcXjIm+v!(XhP>YrL5clld;B;?Z zX|Ngb#ivNePs*{t*A~33J2w`g8yFsH3Qp z5*CL7#2+F=&ILoB*bb`QX?@?BceI1ojIFjZdW7dBH!;y5Lf1=B5fIfImIQ;U)TD zH@Kg^EA+FHhFOpG-05D9-s^cF))4Nlp7SL_oW!XpsAvm{(G8qy4%+F?^k=t`L5N~ZOJR@P@ z`)mJ+{D>OoXghX(Q`ky;I#Ac$v+w8Y@bQor_)yAVHDX+su&>uskEs(h?&qy6T{3mu z7O=5A(Ed58g>hBT)GWg1*AJ5}gq7>O^Ao7`*CD!zrH2#|iX%gxtIG@4xYyB^%1l+A!jY~4gf>_y zbfr#3tKRJTWUkOB%0I_AE$iN&%*8H>)!y&MoX?a7s4oij17$jq;!W@G)JhM&%ce>8 zkr<8gK3Ah3uBi}-lSs`nCuUXeTJNx*_KpGZ-(2Yp>o_aRRqhYJuk_f*9k*Wbp35^D zFeN2i^0!>^K`Z!Y;hkq=7XrRjyry`(y6q*ub%k~E228r-Fp^wJSh#~)=pC`ME4k=V zgoE%&*GM}rZR)l}Jqp2vR-cvr$#m8?c6Qi=*-@$P2B2xE6RW^?4?oH(TG25aPN(De zhq_U0oWn?or#zu-MeF#FF4n?f-MG7X5b=MQckdsIlX?T%j{hZe-z^!o@*e7MMqZjR zV>`8tz-=w|eN+AOiFW%F%JT62O7;DHZ)bN?rS^>xPE>Jx50`=XBQ7;)L^?W%@3@tE z(3|gqIw+LJjM!~h@bYZ*Geb#;$F&BH^P_`ud+d{}PKfc)m(5x7b&n_2M2jcgE84!uvC0f zVbdus6Qx}Rh@8NKuh;xCf5qG&VkJT&eE7Sot#JaQ)wWkdqbI9QdasS6AQ)i{AJPl^ zT9+K!cUjjqqU{=@=!HpFpmtYn;q#fLEW4+8a@?b&TOBPTVf zUuDCJe&~SnI>(6AV8cT$yPRbcYPmlR9}rWoyp~K$yU)U-ktbcW<o~}`v43<@NOmkkIzk8*CEk#&e6*@HrNKmA; zyKhRVE$R-t?CF#U4BnKTjC`rg+baZZVux3suTNBfA=ge)B%`DVU-(HfVV*sIo)j;R3AKHo2yf zOkB<2hGWNM3y`zjaP;mkVbO0FI$wqzqqG(#!==eWIUoVAtln`#suxSXLNy%=->$mo zE!zhZli=eX*OHbE|5EE90-G#<=Lxy8!A8wl<*SnTP~Dakp6xEdol@Z17*nnJ^;U6y zs-|JldY@v}qm~Jz5Y3Df!IJX1V(c%D{ejTubibW=HG8KlpN(U+e;LIKXId@R51Z!k zWNr4{K8+zC_Zoue!Ipuy|7JE;L1+tEK5y%{WdlNt%Rj$qA=sPIn5MzK9;s|-dOfTvO6`42|Lx9p z9d66viK4ezya9d_zJwr+ zwW;P%5}nd;@U-S>J|_{G5U7(}BDUtxV5JNMrE(X79Cjr6AyYlsTm&iJLUNM2ov36&)aQmN!mMv8K@2G+X{pPp3{;zTqGVSeF;iyK2; z1qZ<+11p-OQGBtI4T}HG<#_LD5f#)3Y zTpK@^MrRvUy6PY2fc8ozW9zmmWP~P8=d#tB%HZ(c+Ju5{;yXE+59|JUkvX<~`K-F7 zG;Xl(p<$Z^6<1vEIvt%fXs?!Hl%O++C|N1aPdxVTd%JYTzzklHxz}C)! zJX$8J%Y6C>)On_Xk*;|?KiLyi5ImYPgh7^6Li2nnAb5)W6 literal 0 HcmV?d00001 diff --git a/background/判断路径是否为贪心路径.png b/background/判断路径是否为贪心路径.png new file mode 100644 index 0000000000000000000000000000000000000000..85107103a95bb9fc14f3838e17115e6cd62ef335 GIT binary patch literal 3416 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 T?r$SH00000NkvXXu0mjf^cD_S literal 0 HcmV?d00001 diff --git a/background/增加节点.png b/background/增加节点.png new file mode 100644 index 0000000000000000000000000000000000000000..8a81a388b8727aec5049f1412cca984983b947a1 GIT binary patch literal 2140 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 TFdEe!00000NkvXXu0mjfyUAZ1 literal 0 HcmV?d00001 diff --git a/background/未选择.png b/background/未选择.png new file mode 100644 index 0000000000000000000000000000000000000000..8469c4e298c80e3e84d3f15f9b12c4537f56fed7 GIT binary patch literal 1559 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 literal 0 HcmV?d00001 diff --git a/background/矩形 2.png b/background/矩形 2.png new file mode 100644 index 0000000000000000000000000000000000000000..e4e669c22fee88bd3f4fc12b63bc14f7bf3d1650 GIT binary patch literal 3295 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 literal 0 HcmV?d00001 diff --git a/background/线路增加.png b/background/线路增加.png new file mode 100644 index 0000000000000000000000000000000000000000..bf461961fb558260774dbd6b3f2a7668cdfa1710 GIT binary patch literal 2176 zcmYLKdpy%^8~<%K7E=sGC}s}HVO}P3&cux6okMaypXQKqnn=zTqG-r@IXtrQRtgh2 zj7Y7b=1@Y<5(z0RC7$)q^WLA&_x@h@eSPnLuIv6>Uy1|4T2%P3FaQ9eI2$aH&vSgJ z!36pHC+d~~pFkIh)~A3Lw%ii`fDLrT1=-sJ>iif6fU-jY$R7zGhxq^iff^7Xz-Q2( zyBhGn*|{2le`B>j!b`^4S^yyQ8HcrSN&u~Ro(nH_mfQ_m93r^erV>M@4%KE}rQZ{# z`GJu+*CDrA9jFl6H`@AOyUJ;gG>fBp;81L|?|(BCL>2Jng;q6m&0?yjm|9zSRcm8W zS!mF9@Y9$hiG=@*r>tIBosHUEt6FJ@wcw6Jd0p@BSk#VYM)THgOT4v%gDmIzloaHe zC9g>}qI~;yPMz({=^caN2B&6VipXI)VggMk!3eB@%e$}hi-07~8p#q{T5IpScw z1a4J%WN;n_{os!Z47%_lLdP4HK_3;%Q3P|VjIw^(KT!06WmxpWqy-h0^&x(gE8=MC z;;l$a1vvP0FB&-4t|*3w1EY@tRMlcaxXrZ-c{xJU*7uZy3+Wkk4Jpo1x} zcU{dk0WgrY>Hdn7g2ws+i^m|Io0SilcGR*i4_gZ!DCo_!m7h@*DMia0+&lVLCp3>NY|tj8wry0QzjqlC^<~`rEL!x(c@5=8}l5WiVjj3@<=8cgjm&(ZI->HeotMLEDlVE|H z%t#jy>ih%O`YpfppvRzhoeWN3ggBaf&_OxFRD<)B(iZ-Atcj!U#z{uH8-3ibsXSMt ztQ6u@tnMqAv;1Y*MaccO<5?ft&T0j|%-C_QY{IG<6hnS-Gk1^yEUu0@W55XcU_S^v z%`ws}xr1E+1ohlO)h-YH_%derKw+7>Ja7nWHN23f19(d^YnFS?roA9L(%*F1?gWYq zGP)c6`@6@(2xKqoj|D{JR|(I_+P?>(Ga@ME(PSR<;g0ZY1iFH_q}qAem0r%i-wLGc zK$Dry7+y4;qZ5~&?F63efZ8f zf1=ko^T-WTw}H5oH?(h!j-9Hqy$p5jsTL0+{`oe+->Z>?8I0cl_5aEwb=%J$V~{^ZeIwQ&RksUhkiWn6!Dd1Oc$%RyV1Y(pJGPz)!+hOVzm zc|MwDDc39KIxQ}2vA3UF+1J$qsQ;UmeM z=)n>oCEL7z_V3k7bCcA!Ar_%`abahsGG%jc1N>hl*jEtBThb<}N+31eNfj=Sh;Nzr zykQ-vf2ywprc>NLu}TK(=D@7C`wTxkpJGX(+U`O3^i``%;Ula2>?)PClqf27Td)~7 zw~gl{W6k-Lptx80X^9?)j@NExM=R$slR4(F;j_L77>M(%blWJ}p$KJH{L*t{m|__w zfA00dfLwmAjb|IJt!n01^MaIjQ~49x&c#-ed)->wbW((St8Pg$5=CDR^Ccq@ioW+R zw1xMRHArZG(%5y#I!bGiEtHv9<1Ep#{Db=zK7 z5Kh+{Cw`>7>56j}LQ?~hhA>ERL_)t_WnV|kgvwet2arW}3w~~@MvQFrL80j(icV|p3>yqg~QdY4H?HUQLD#|)H4?%0g|uv7S7PG z5>dD+hhvRpwB{bhMywo@ywUFeuvjRrId>GpjG2%zj*_vDyME5S2RtG|bl;FV^PAnS zIf+?Le8#@qZc{+hS@@WAM&lXTSi?bGD>2Ft!@aa0Q%WH5kaOxu$8WX1ZLVqZyi16; zBs@vT*hhJ$am=z9pPOC>p;S^8Rh)V%PpU5!Hb(yM=hu)V*NED)RHfF z!8fx13w|6pd0bS`vR5o52{LT5o6i0)$8uJlGE=4bIO546Ro? zKOJH|Et2SaEJqAj4e{@bTGn%hd^T{wvaes-6TlF~-DR(-tN{Flvm#(yPWhz$1MdpV AtN;K2 literal 0 HcmV?d00001 diff --git a/background/编组9.png b/background/编组9.png new file mode 100644 index 0000000000000000000000000000000000000000..c053d6a57935bae4502ca74e64e23abfc3efa727 GIT binary patch literal 4890 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 auV@dB_R*c*l-mDJI7^DjeXA5P2>5>$JFlw% literal 0 HcmV?d00001 diff --git a/background/背景.png b/background/背景.png new file mode 100644 index 0000000000000000000000000000000000000000..baa75cd2e9e8021d82f40336bac91a7ab2d9d756 GIT binary patch literal 93125 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 10 and abs(value1) > 10: + tk.messagebox.askokcancel(title='错误', message='请重新输入正确的编号!') + return + else: + if value1 < value2: + str_route = f'{value1}-{value2}' + else: + str_route = f'{value2}-{value1}' + no = 0 + for item in d.Edges: + if str_route == item[5]: + no = G.node_edge(value1 - 1, value2 - 1) + if d.Edges[no][3] == 0: + tk.messagebox.askokcancel(title='提醒', message='当前路线不存在!') + return + top = creat_window(str_route + '路径参数修改') # 创建弹出窗口 + distance_entry = create_input_box(top, "路程/公里:(1-99)", d.Edges[no][6]) # 创建一个输入框,获取卷积核大小 + time_entry = create_input_box(top, "时间/h:(1-99)", d.Edges[no][7]) # 创建一个输入框,获取卷积步长 + def get_input(): # 创建 + ···与前面way_add里get_input相似 + button = tk.Button(top, text="获取信息", command=get_input) + button.pack() + except ValueError: + tk.messagebox.askokcancel(title='错误', message='请输入正确的城市编号!') \ No newline at end of file diff --git a/demo11.py b/demo11.py new file mode 100644 index 0000000..92d21cb --- /dev/null +++ b/demo11.py @@ -0,0 +1,21 @@ +# 导入aip模块,这是一个来自百度AI平台的Python SDK,可以用来调用百度NLP(自然语言处理)服务 +from aip import AipNlp +# 定义一个名为address的函数,它接受一个字符串参数addr,默认为空字符串 +def address(addr=''): + # 在这个函数中定义了APP_ID、API_KEY和SECRET_KEY三个变量,这些变量是你在百度AI平台上申请的凭证 + APP_ID = '41768293' + API_KEY = 'CAnsdV8e40GQtjhK4qHkucUO' + SECRET_KEY = 'jU1NbbnwrhTahngr2p3cz8p2EIAa8zWj' + + # 使用凭证创建一个AipNlp的客户端对象,这个对象会用来发送请求到百度AI平台 + client = AipNlp(APP_ID, API_KEY, SECRET_KEY) + """ 调用词法分析 """ + # 调用客户端对象的address方法,将参数addr发送到百度AI平台进行地址识别,返回的结果保存在变量re中 + re = client.address(addr) + # 返回识别的结果 + + return re + + # 调用address函数,参数为你要识别的具体地址,并将返回的结果打印出来 + +print(address('海南省三亚市吉阳区迎宾大道学院路三亚学院')) \ No newline at end of file diff --git a/main.py b/main.py index 2871150..dbd3a0e 100644 --- a/main.py +++ b/main.py @@ -1,248 +1,61 @@ -# -*- encoding: utf-8 -*- -from NODE import * -from SOLVE import * -from EDGE import * -from WAYS import * - -N = Node() -E = Edge() -W = Ways() - -mode = tk.IntVar(value=1) -# 目前的操作状态: -# 1为节点操作 -# 2为链接操作 -# 3为路径操作 -# 默认为1 - - -RIGHT = -1 -B = tk.Button(frame, text="修改", command=lambda: N.node_set()) -B.place(x=180, y=10, width=50, height=30) - - -def left1(event): - # 查找鼠标左键按下时位置是否在某个节点内 - n = -1 - for node in d.Nodes: - if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: - n = node[0] - - # n为点击的节点,若没有则为-1 - m = mode.get() # 查看现在操作类型 - if m == 1 and not n == -1: - # 节点操作 - - N.node_mark(n) - elif m == 2 and not n == -1: - # 链接操作 - if len(E.mark) == 0: - E.mark.append(n) - E.node_mark1(E.mark[0]) - elif len(E.mark) == 1: - E.mark.append(n) - E.node_mark1(E.mark[0]) - E.node_mark2(E.mark[1]) - elif not n == E.mark[1]: - n1 = E.mark[1] - E.mark.clear() - E.mark.append(n1) - E.mark.append(n) - E.node_mark1(E.mark[0]) - E.node_mark2(E.mark[1]) - # 路径操作 - elif m == 3 and not n == -1: - W.user_way_add(n) - # 自动判断操作 - elif m == 4 and not n == -1: - if len(solve.mark) == 0: - solve.mark.append(n) - solve.node_mark1(solve.mark[0]) - elif len(solve.mark) == 1: - solve.mark.append(n) - solve.node_mark1(solve.mark[0]) - solve.node_mark2(solve.mark[1]) - elif not n == solve.mark[1]: - n1 = solve.mark[1] - solve.mark.clear() - solve.mark.append(n1) - solve.mark.append(n) - solve.node_mark1(solve.mark[0]) - solve.node_mark2(solve.mark[1]) - - -def onRightButtonUp(event): - global RIGHT - # 查找鼠标右键按下时位置是否在某个节点内 - n = -1 - for node in d.Nodes: - if dir2([event.x, event.y], [node[3][0], node[3][1]]) < 20 * 20: - n = node[0] - if n == -1: - menu.post(event.x_root, event.y_root) - else: - RIGHT = n - menu_node.post(event.x_root, event.y_root) - - -# 右键菜单 -def m_node(): - mode.set(1) - W.clear() - N.node_mark_display() - - -def m_edge(): - mode.set(2) - W.clear() - E.edge_mark_display() - - -def m_ways(): - mode.set(3) - W.user_way_clear() - W.way_mark_display() - - -def m_info(): - d.edgeinfo ^= 1 - G.draw() - - -def m_solve(): - W.clear() - mode.set(4) - solve.Solve_display() - - -def graph_init(): - nodes = random.randint(4, 10) - N.node_init(nodes) - G.draw() - N.node_mark_display() - - -def sourc_set(num): - W.clear() - d.source = num - N.node_init(nodes) - G.draw() - - -def node_del(num): - N.node_mark(num) - N.node_del_exact() - - -def node_name(num): - N.node_mark(num) - N.node_rename() - - -def node_condition(num): - d.Nodes[num][4] ^= 1 - G.draw() - - -menu = tk.Menu(root, tearoff=0) -menu.add_command(label='节点操作', command=m_node) -menu.add_command(label='连接操作', command=m_edge) -menu.add_command(label='路径操作', command=m_ways) -menu.add_command(label='显示/隐藏链接信息', command=m_info) - -menu.add_command(label='自动判断相关', command=m_solve) -fmenu1 = Menu(menu, tearoff=False) -fmenu1.add_command(label='使用模拟数据', command=lambda: sourc_set(1)) -fmenu1.add_command(label='使用黑龙江省数据', command=lambda: sourc_set(2)) -fmenu1.add_command(label='使用辽宁省数据', command=lambda: sourc_set(3)) -menu.add_cascade(label="切换数据源", menu=fmenu1) - -menu_node = tk.Menu(root, tearoff=0) -menu_node.add_command(label='删除节点', command=lambda: node_del(RIGHT)) -menu_node.add_command(label='切换必经/非必经', command=lambda: node_condition(RIGHT)) -menu_node.add_command(label='重命名节点', command=lambda: node_name(RIGHT)) -# menu_node.add_command(label='显示/隐藏链接信息', command=m_info) - -cv.bind('', left1) -cv.bind('', onRightButtonUp) - - -# 释放右键时打开菜单 - -def auto_opera_mu(menu: tk.Menu): - """TSP问题自动生成与自动求解相关""" - menu_node = Menu(menu, tearoff=False) - menu_node.add_command(label="自动随机产生一个TSP问题", command=lambda: graph_init()) - menu_node.add_command(label="重新生成", command=lambda: N.node_refresh()) - menu_node.add_command(label="自动求解最优路径-遍历", command=lambda: solve.dfs_panel()) - menu_node.add_command(label="自动求解优化路径-贪心", command=lambda: solve.greedy_panel()) - menu_node.add_command(label="检查是否存在路径", command=lambda: solve.check()) - - # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 - menu.add_cascade(label="TSP问题自动生成与自动求解相关", menu=menu_node) - - -def node_opera_mu(menu: tk.Menu): - """节点操作""" - - # fmenu1 = Menu(menu, tearoff=False) - # for i in range(10): - # fmenu1.add_command(label='选择节点 ' + str(i + 1), command=lambda num=i: node_mark(num)) - - menu_node = Menu(menu, tearoff=False) - menu_node.add_command(label="添加一个节点", command=lambda: N.node_add()) - menu_node.add_command(label="删除一个节点", command=lambda: N.node_del()) - # menu_node.add_cascade(label="选择一个节点", menu=fmenu1) - menu_node.add_command(label="选择一个节点", command=lambda: N.node_mark_display()) - # menu_node.add_command(label="停止标记一个节点", command=lambda: node_mark_display1()) - # menu_node.add_command(label="设置节点标签", command=lambda:node_rename()) - - # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 - menu.add_cascade(label="节点操作", menu=menu_node) - - -def edge_opera_mu(menu: tk.Menu): - """连接操作""" - - # fmenu1 = Menu(menu, tearoff=False) - # for i in range(10): - # fmenu1.add_command(label='选择另一个节点 ' + str(i + 1), command=lambda num=i: node_mark2(num)) - # - menu_node = Menu(menu, tearoff=False) - # menu_node.add_cascade(label="选择另一个节点", menu=fmenu1) - - menu_node.add_command(label="选择一个连接", command=lambda: E.edge_mark_display()) - - # menu_node.add_command(label="建立一个连接",command=lambda :edge_add()) - menu_node.add_command(label="删除所有连接", command=lambda: E.edge_delall()) - menu_node.add_command(label="删除所有多余连接", command=lambda: E.newedge_del_all(1)) - # menu_node.add_command(label="设置连接标签", command=lambda: edge_rename()) - # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 - menu.add_cascade(label="连接操作", menu=menu_node) - - -def path_opera_mu(menu: tk.Menu): - """路径操作""" - menu_node = Menu(menu, tearoff=False) - menu_node.add_command(label="开始标记路径", command=lambda: W.way_mark_display()) - menu_node.add_command(label="撤销一步路径", command=lambda: W.user_way_del()) - menu_node.add_command(label="取消所有路径标记", command=lambda: W.user_way_clear()) - # 在主目录菜单上新增"文件"选项,并通过menu参数与下拉菜单绑定 - menu.add_cascade(label="路径操作", menu=menu_node, ) - - -auto_opera_mu(main_menu) -node_opera_mu(main_menu) -edge_opera_mu(main_menu) -path_opera_mu(main_menu) - - -def main(): - G = Graph() - G.draw() - sourc_set(2) - mainloop() - - -if __name__ == '__main__': - main() +import sys + +import numpy as np + + +def tsp_nearest_neighbor1(distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = sys.maxsize + nearest_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + nearest_city = city + + if nearest_city is not None: + path.append(nearest_city) + visited[nearest_city] = True + + path.append(0) # 回到起始城市形成闭合路径 + return path +def tsp_nearest_neighbor(distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = sys.maxsize + nearest_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + nearest_city = city + + if nearest_city is not None: + path.append(nearest_city) + visited[nearest_city] = True + + path.append(0) # 回到起始城市形成闭合路径 + return path +# 示例用法 +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +path = tsp_nearest_neighbor1(distances) +print(path) \ No newline at end of file diff --git a/test/DP.py b/test/DP.py new file mode 100644 index 0000000..55d25cc --- /dev/null +++ b/test/DP.py @@ -0,0 +1,49 @@ +import numpy as np + +def tsp_dp(distances): + n = len(distances) + dp = np.full((1 << n, n), np.inf) + path = np.zeros((1 << n, n), dtype=int) + + for i in range(n): + dp[1 << i][i] = 0 + + for S in range(1, 1 << n): + for j in range(n): + if not (S & (1 << j)): + continue + for k in range(n): + if k == j or not (S & (1 << k)): + continue + if dp[S][j] > dp[S - (1 << j)][k] + distances[k][j]: + dp[S][j] = dp[S - (1 << j)][k] + distances[k][j] + path[S][j] = k + + final_path = [] + S = (1 << n) - 1 + j = 0 + while S: + final_path.append(j) + k = path[S][j] + S -= 1 << j + j = k + final_path.append(0) + + ans = float('inf') + for j in range(n): # 枚举所有结束点 + if distances[j][0] != np.inf: # 如果结束点j与起点0相连 + ans = min(ans, dp[(1 << n) - 1][j] + distances[j][0]) + + return ans, final_path +# 示例使用 +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) +min_cost, min_path = tsp_dp(distances) +print("Minimum cost:", min_cost) +print("Minimum path:", min_path) \ No newline at end of file diff --git a/test/Prin.py b/test/Prin.py new file mode 100644 index 0000000..9b6f010 --- /dev/null +++ b/test/Prin.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/3 17:42 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : Prin.py + +import numpy as np + + +def prim_exists_path(distances): + num_cities = distances.shape[0] + visited = set() + pq = [] + start_node = 0 + + visited.add(start_node) + for neighbor in range(num_cities): + if neighbor != start_node and np.isfinite(distances[start_node, neighbor]): + pq.append((distances[start_node, neighbor], start_node, neighbor)) + + pq.sort(key=lambda x: x[0]) + + while pq: + weight, node1, node2 = pq.pop(0) + if node2 not in visited: + visited.add(node2) + for neighbor in range(num_cities): + if neighbor != node2 and np.isfinite(distances[node2, neighbor]): + pq.append((distances[node2, neighbor], node2, neighbor)) + + pq.sort(key=lambda x: x[0]) + + return len(visited) == num_cities + + +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +print(prim_exists_path(distances)) \ No newline at end of file diff --git a/test/demo1.py b/test/demo1.py new file mode 100644 index 0000000..33389fa --- /dev/null +++ b/test/demo1.py @@ -0,0 +1,76 @@ +import random + +def initialize_population(num_points, pop_size): + population = [] + for _ in range(pop_size): + individual = list(range(num_points)) + random.shuffle(individual) + population.append(individual) + return population + +def evaluate_fitness(individual, distances): + fitness = 0 + num_points = len(individual) + for i in range(num_points): + city1 = individual[i] + city2 = individual[(i + 1) % num_points] + fitness += distances[city1][city2] + return fitness + +def crossover(parent1, parent2): + child = [-1] * len(parent1) + start_idx = random.randint(0, len(parent1)-1) + end_idx = random.randint(start_idx+1, len(parent1)) + child[start_idx:end_idx] = parent1[start_idx:end_idx] + for city in parent2: + if city not in child: + for i in range(len(child)): + if child[i] == -1: + child[i] = city + break + return child + +def mutate(individual): + idx1, idx2 = random.sample(range(len(individual)), 2) + individual[idx1], individual[idx2] = individual[idx2], individual[idx1] + +def genetic_algorithm(distances, pop_size, num_generations): + num_points = len(distances) + population = initialize_population(num_points, pop_size) + + for generation in range(num_generations): + population_fitness = [] + for individual in population: + fitness = evaluate_fitness(individual, distances) + population_fitness.append((individual, fitness)) + + population_fitness.sort(key=lambda x: x[1]) # 按适应度排序 + elite_individual = population_fitness[0][0] + new_population = [elite_individual] + + while len(new_population) < pop_size: + parent1, parent2 = random.choices(population_fitness, k=2) + child = crossover(parent1[0], parent2[0]) + mutate(child) + new_population.append(child) + + population = new_population + + best_individual = population_fitness[0][0] + return best_individual + +# 示例使用 +# distances为距离矩阵 +distances = [ + [0, 50, 99, 0, 64, 0], + [50, 0, 12, 45, 74, 13], + [99, 12, 0, 25, 0, 61], + [0, 45, 25, 0, 45, 47], + [64, 74, 0, 45, 0, 0], + [0, 13, 61, 47, 0, 0] +] +population_size = 100 +num_generations = 1000 + +best_solution = genetic_algorithm(distances, population_size, num_generations) +print(best_solution) \ No newline at end of file diff --git a/test/demo10.py b/test/demo10.py new file mode 100644 index 0000000..75e42ce --- /dev/null +++ b/test/demo10.py @@ -0,0 +1,46 @@ +import numpy as np + +inf = float('inf') + +distances = np.array([ + [inf, 72, inf, inf, 81, 67], + [72, inf, 26, inf, 61, inf], + [inf, 26, inf, 53, 97, inf], + [inf, inf, 53, inf, inf, inf], + [81, 61, 97, inf, inf, 38], + [67, inf, inf, inf, 38, inf] +]) + +def find_cycles(start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == inf: + return False + if node in path: + return False + return True + + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != inf: + cycles.append(path.copy()) + path.pop() + + find_paths(start) + return cycles + +all_cycles = find_cycles(0, distances) + +if all_cycles: + print("经过全部节点能返回起点的回路的全部路径为:") + for cycle in all_cycles: + print(cycle + [cycle[0]]) # 添加起点以形成完整的回路 +else: + print("不存在经过全部节点能返回起点的回路") \ No newline at end of file diff --git a/test/demo11.py b/test/demo11.py new file mode 100644 index 0000000..29d6ab6 --- /dev/null +++ b/test/demo11.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/9 11:26 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo11.py + +import os + +# 遍历指定文件夹 +def rename_files(folder_path): + for root, dirs, files in os.walk(folder_path): + for file in files: + # 检查文件类型是否为图片类型(这里使用了简单的判断方式) + if file.endswith('.jpg') or file.endswith('.png'): + # 获取当前文件所在的子文件夹名称 + subdir_name = os.path.basename(root) + # 组合新的文件名,添加子文件夹名称前缀 + new_file_name = subdir_name + '_' + file + # 使用 os.rename() 方法来重命名文件 + os.rename(os.path.join(root, file), os.path.join(root, new_file_name)) + +# 调用函数来重命名文件 +rename_files('D:/weixin/WeChat Files/wxid_f2yvfdxbyuag22/FileStorage/File/2023-11/球/') \ No newline at end of file diff --git a/test/demo12.py b/test/demo12.py new file mode 100644 index 0000000..b16d0ab --- /dev/null +++ b/test/demo12.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/9 11:28 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo12.py + +import os +import shutil + +# 遍历指定文件夹并复制图片文件到新的文件夹 +def copy_images(folder_path, target_folder_path): + # 创建目标文件夹(如果它不存在) + if not os.path.exists(target_folder_path): + os.makedirs(target_folder_path) + + for root, dirs, files in os.walk(folder_path): + for file in files: + # 检查文件类型是否为图片类型(这里使用了简单的判断方式) + if file.endswith('.jpg') or file.endswith('.png'): + # 构造源文件的完整路径 + source_file_path = os.path.join(root, file) + # 构造目标文件的完整路径 + target_file_path = os.path.join(target_folder_path, file) + # 使用 shutil 模块的 copy2() 函数复制文件(保留源文件的元数据) + shutil.copy2(source_file_path, target_file_path) + +# 调用函数来复制图片文件 +copy_images('D:/weixin/WeChat Files/wxid_f2yvfdxbyuag22/FileStorage/File/2023-11/球/', 'K:/工作/traveler/background/') \ No newline at end of file diff --git a/test/demo13.py b/test/demo13.py new file mode 100644 index 0000000..1f2ce65 --- /dev/null +++ b/test/demo13.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/9 11:54 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo13.py + +import os + +# 读取文件夹中的图像文件名到列表 +def get_image_filenames(folder_path): + image_filenames = [] + + for file_name in os.listdir(folder_path): + # 检查文件类型是否为图像类型(这里使用了简单的判断方式) + if file_name.endswith('.jpg') or file_name.endswith('.png'): + # 将文件名添加到列表中 + image_filenames.append(file_name) + + return image_filenames + +# 调用函数来获取图像文件列表 +image_files = get_image_filenames('K:/工作/traveler/background/') +print(image_files) \ No newline at end of file diff --git a/test/demo2.py b/test/demo2.py new file mode 100644 index 0000000..a63d93a --- /dev/null +++ b/test/demo2.py @@ -0,0 +1,106 @@ +import numpy as np + +class AntColonyOptimization: + def __init__(self, distances, num_ants=10, num_iterations=100, alpha=1, beta=1, evaporation=0.5, q0=0.9): + self.distances = distances + self.num_ants = num_ants + self.num_iterations = num_iterations + self.alpha = alpha + self.beta = beta + self.evaporation = evaporation + self.q0 = q0 + + def optimize(self): + num_cities = len(self.distances) + pheromone = np.ones((num_cities, num_cities)) + best_path = None + best_length = float('inf') + + for _ in range(self.num_iterations): + paths = [] + lengths = [] + + for _ in range(self.num_ants): + path = self.construct_path(pheromone) + length = self.calculate_length(path) + paths.append(path) + lengths.append(length) + + if length < best_length: + best_length = length + best_path = path + + self.update_pheromone(pheromone, paths, lengths) + + return best_path + + def construct_path(self, pheromone): + num_cities = len(pheromone) + path = [0] # 起始城市为0 + visited = np.zeros(num_cities, dtype=bool) + visited[0] = True + + for _ in range(num_cities - 1): + probs = self.calculate_probs(pheromone, path[-1], visited) + next_city = self.choose_next_city(probs) + + path.append(next_city) + visited[next_city] = True + + return path + + def calculate_probs(self, pheromone, curr_city, visited): + num_cities = len(pheromone) + probs = np.zeros(num_cities) + + denominator = 0 + for city in range(num_cities): + if not visited[city]: + denominator += pheromone[curr_city, city] ** self.alpha + (1.0 / self.distances[curr_city, city]) ** self.beta + + for city in range(num_cities): + if not visited[city]: + numerator = pheromone[curr_city, city] ** self.alpha + (1.0 / self.distances[curr_city, city]) ** self.beta + probs[city] = numerator / denominator + + return probs + + def choose_next_city(self, probs): + q = np.random.rand() + + if q < self.q0: + next_city = np.argmax(probs) + else: + next_city = np.random.choice(len(probs), p=probs) + + return next_city + + def calculate_length(self, path): + length = 0 + for i in range(len(path) - 1): + length += self.distances[path[i], path[i+1]] + + return length + + def update_pheromone(self, pheromone, paths, lengths): + pheromone *= self.evaporation + + for i, path in enumerate(paths): + for j in range(len(path) - 1): + city1 = path[j] + city2 = path[j+1] + pheromone[city1, city2] += 1.0 / lengths[i] + pheromone[city2, city1] += 1.0 / lengths[i] + +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +aco = AntColonyOptimization(distances) +best_path = aco.optimize() +print(best_path) \ No newline at end of file diff --git a/test/demo3.py b/test/demo3.py new file mode 100644 index 0000000..bee3ea6 --- /dev/null +++ b/test/demo3.py @@ -0,0 +1,66 @@ +import numpy as np + +def tsp_backtracking(distances): + num_cities = len(distances) + path = [0] # 起始城市为0 + visited = [False] * num_cities + visited[0] = True + min_distance = float('inf') + shortest_path = None + + def backtrack(curr_city, curr_distance, visited, path): + nonlocal min_distance, shortest_path + if len(path) == num_cities: + # 到达所有城市,更新最短路径 + if curr_distance + distances[curr_city][0] < min_distance: + min_distance = curr_distance + distances[curr_city][0] + shortest_path = path + [0] + else: + for next_city in range(num_cities): + if not visited[next_city]: + # 选择下一个未访问的城市 + visited[next_city] = True + path.append(next_city) + new_distance = curr_distance + distances[curr_city][next_city] + # 剪枝条件:当前路径已经大于最短路径,不继续搜索 + if new_distance < min_distance: + backtrack(next_city, new_distance, visited, path) + # 回溯 + visited[next_city] = False + path.pop() + backtrack(0, 0, visited, path) + return shortest_path +# 示例用法 +# distances = np.array([ +# [np.inf, 50, 99, np.inf, 64, np.inf], +# [50, np.inf, 12, 45, 74, 13], +# [99, 12, np.inf, 25, np.inf, 61], +# [np.inf, 45, 25, np.inf, 45, 47], +# [64, 74, np.inf, 45, np.inf, np.inf], +# [np.inf, 13, 61, 47, np.inf, np.inf] +# ]) +# inf = np.inf +# distances = np.array( +# [[inf, 69, 48, inf, 41, 78], +# [69, inf, 48, 53, 91, inf], +# [48, 48, inf, 19, 86, inf], +# [inf, 53, 19, inf, 96, 10], +# [41, 91, 86, 96, inf, inf], +# [78, inf, inf, 10, inf, inf]] +# ) + +inf = float('inf') + +distances = np.array([ + [inf, 72, inf, inf, 81, 67], + [72, inf, 26, inf, 61, inf], + [inf, 26, inf, 53, 97, inf], + [inf, inf, 53, inf, inf, inf], + [81, 61, 97, inf, inf, 38], + [67, inf, inf, inf, 38, inf] +]) +path = tsp_backtracking(distances) +if path: + print(path) +else: + print('不存在') \ No newline at end of file diff --git a/test/demo5.py b/test/demo5.py new file mode 100644 index 0000000..6629168 --- /dev/null +++ b/test/demo5.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/31 11:01 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo5.py + +import numpy as np + +inf = float('inf') + +distances = np.array([[inf, 71, 88, 28, 67, inf], [71, inf, inf, inf, 51, 98], [88, inf, inf, 70, inf, inf], [28, inf, 70, inf, 90, 75], [67, 51, inf, 90, inf, inf], [inf, 98, inf, 75, inf, inf]]) + +def find_hamiltonian_cycles(start, distances): + n = len(distances) + path = [start] + cycles = [] + + def is_valid(node, position): + if distances[path[position - 1]][node] == inf: + return False + if node in path: + return False + return True + + def find_paths(node): + for next_node in range(n): + if is_valid(next_node, len(path)): + path.append(next_node) + if len(path) < n: + find_paths(next_node) + elif len(path) == n and distances[path[-1]][start] != inf: + cycles.append(path + [start]) + path.pop() + + find_paths(start) + return cycles + +all_cycles = find_hamiltonian_cycles(0, distances) + +if all_cycles: + print("经过全部节点能返回起点的回路的全部路径为:") + for cycle in all_cycles: + print(cycle) # 输出所有回路路径 +else: + print("不存在经过全部节点能返回起点的回路") + diff --git a/test/demo6.py b/test/demo6.py new file mode 100644 index 0000000..1ae1e8f --- /dev/null +++ b/test/demo6.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/3 17:21 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : demo6.py + +import sys +import itertools + +import numpy as np + + +def tsp_dp(distances): + n = len(distances) + dp = [[sys.maxsize] * n for _ in range(1 << n)] + dp[1][0] = 0 + + for mask in range(1, 1 << n): + for current in range(n): + if mask & (1 << current): + for previous in range(n): + if previous != current and mask & (1 << previous): + dp[mask][current] = min(dp[mask][current], dp[mask ^ (1 << current)][previous] + distances[previous][current]) + + shortest_path = sys.maxsize + end_mask = (1 << n) - 1 + last_city = -1 + + for i in range(1, n): + if shortest_path > dp[end_mask][i] + distances[i][0]: + shortest_path = dp[end_mask][i] + distances[i][0] + last_city = i + + path = [] + mask = end_mask + while last_city >= 0: + path.append(last_city) + new_mask = mask ^ (1 << last_city) + last_city = -1 + for i in range(n): + if new_mask & (1 << i): + if last_city == -1 or dp[mask][last_city] + distances[last_city][i] == dp[mask][i]: + last_city = i + mask = new_mask + + path.append(0) + path.reverse() + + return shortest_path, path +# +# # 测试代码 +# distances = [ +# [0, 2, 9, 10], +# [1, 0, 6, 4], +# [15, 7, 0, 8], +# [6, 3, 12, 0] +# ] + + + +# 测试代码 +distances = np.array([ + [np.inf, 80, 99, np.inf, 64, np.inf], + [80, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +shortest_path, path = tsp_dp(distances) +print("最短路径长度为:", shortest_path) +print("最短路径为:", path) \ No newline at end of file diff --git a/test/demo7.py b/test/demo7.py new file mode 100644 index 0000000..2a7a1e0 --- /dev/null +++ b/test/demo7.py @@ -0,0 +1,119 @@ +from docx import Document +from docx.oxml import OxmlElement +from docx.oxml.ns import qn +from operator import eq + +flag = True + +def get_paragraph_shading(p): + """ + Get paragraph`s shading + Usage: + get_paragraph_shading(paragraph) + """ + p_pr = p._p.get_or_add_pPr() # 获取段落的pPr + # print(p_pr) + p_shd = p_pr.first_child_found_in("w:shd") # 找到 'w:shd' 标签 + # 如果不存在,就创建 + if p_shd is None: + p_shd = OxmlElement('w:shd') + p_pr.append(p_shd) + # 获取各属性的值 + val = p_shd.get(qn('w:val')) + # color = p_shd.get(qn('w:color')) + fill = p_shd.get(qn('w:fill')) + print([val, fill]) + return [val, fill] + +def get_paragraph_boders(p): + """ + Get paragraph`s boders + Usage: + get_paragraph_boders(paragraph) + """ + p_pr = p._p.get_or_add_pPr() # 获取段落的pPr + p_borders = p_pr.first_child_found_in("w:pBdr") # 找到 'p_borders' 标签 + # 如果不存在,就创建 + if p_borders is None: + p_borders = OxmlElement('w:pBdr') + p_pr.append(p_borders) + # 获取各属性的值 + borders = [] + for edge in ('left', 'top', 'right', 'bottom'): + element = p_borders.find(qn('w:{}'.format(edge))) + if element is None: + element = OxmlElement('w:{}'.format(edge)) + p_borders.append(element) + border = [] + for key in ["sz", "val", "color"]: + temp = str(element.get(qn('w:{}'.format(key)))) + if temp == 'none' or temp == '0' or temp == 'auto' or temp == 'NONE': + temp = 'None' + border.append(temp) + borders.append(border) + return borders + +def get_text_borders(p): + t_pr = p.runs[0].element.rPr + + t_bor = t_pr.first_child_found_in("w:bdr") # 找到 'w:bdr' 标签 + # 如果不存在,就创建 + if t_bor is None: + t_bor = OxmlElement('w:bdr') + t_pr.append(t_bor) + # # 获取各属性的值 + val = t_bor.get(qn('w:val')) + color = t_bor.get(qn('w:color')) + sz = t_bor.get(qn('w:sz')) + return [val, sz, color] + +def Comp(name1, name2): + """ + Compare the diferences of two files + Usage: + Comp(file_name1, file_name2) + """ + global flag + ff = 'K:/工作/traveler/test/' + # s = Document(name1) + # t = Document(name2) + s = Document(ff + name1) + t = Document(ff + name2) + + # 获取段落 + x = s.paragraphs + + y = t.paragraphs + + l = len(x) + for i in range(l-1): + s_shd = get_paragraph_shading(x[i]) + t_shd = get_paragraph_shading(y[i]) + s_border = get_paragraph_boders(x[i]) + t_border = get_paragraph_boders(y[i]) + if not eq(s_shd, t_shd): + print('效果对比出错,问题:',s_shd,'应该修改为:',t_shd) + print(1) + flag = False + break + if not eq(s_border, t_border): + print(1) + print('效果对比出错,问题:',s_border,'应该修改为:',t_border) + flag = False + break + + # 判断标题的文字边框 + if not eq(get_text_borders(x[0]), get_text_borders(y[0])): + print('文字边框设置出错,问题:',get_text_borders(x[0]),'应该修改为:',get_text_borders(y[0])) + flag = False + +try: + Comp('中国共产党党史.docx', '美化文档.docx') + + if flag: + print('Accepted!') + else: + print('文件内容错误,请调整后重试!') +except Exception as e: + print(e) + print('请按步骤创建、操作文本后重试!') \ No newline at end of file diff --git a/test/demo8.py b/test/demo8.py new file mode 100644 index 0000000..3453154 --- /dev/null +++ b/test/demo8.py @@ -0,0 +1,30 @@ +import numpy as np +import networkx as nx + +inf = float('inf') + + +def find_hamiltonian_cycles(distances): + num_nodes = distances.shape[0] + graph = nx.DiGraph() + for i in range(num_nodes): + for j in range(num_nodes): + if distances[i][j] != inf: + graph.add_edge(i, j, weight=distances[i][j]) + + hamiltonian_cycles = list(nx.simple_cycles(graph)) + hamiltonian_cycles = [cycle + [cycle[0]] for cycle in hamiltonian_cycles if len(cycle) == num_nodes - 1] + + return hamiltonian_cycles + + +# distances = np.array([[inf, 69, 48, inf, 41, 78], [69, inf, 48, 53, 91, inf], [48, 48, inf, 19, 86, inf], [inf, 53, 19, inf, 96, 10], [41, 91, 86, 96, inf, inf], [78, inf, inf, 10, inf, inf]]) +distances = np.array([[inf, 94, 67, 65, inf], [94, inf, inf, 41, inf], [67, inf, inf, inf, 65], [65, 41, inf, inf, 54], [inf, inf, 65, 54, inf]]) +all_hamiltonian_cycles = find_hamiltonian_cycles(distances) + +if all_hamiltonian_cycles: + print("存在的哈密尔顿回路路径为:") + for cycle in all_hamiltonian_cycles: + print(cycle) +else: + print("不存在哈密尔顿回路") \ No newline at end of file diff --git a/test/demo9.py b/test/demo9.py new file mode 100644 index 0000000..6b23864 --- /dev/null +++ b/test/demo9.py @@ -0,0 +1,42 @@ +import numpy as np + +inf = float('inf') + + +def find_all_cycles(distances): + num_nodes = distances.shape[0] + visited = [False] * num_nodes + cycles = [] + + def dfs(node, start, path): + visited[node] = True + for neighbor in range(num_nodes): + if distances[node, neighbor] != inf: + if neighbor == start and len(path) > 1: + cycles.append(path + [start]) + elif not visited[neighbor]: + dfs(neighbor, start, path + [neighbor]) + visited[node] = False + + for start_node in range(num_nodes): + dfs(start_node, start_node, [start_node]) + + return cycles + + +distances = np.array([ + [inf, inf, 21, inf, 53], + [inf, inf, 77, 84, 93], + [21, 77, inf, inf, 53], + [inf, 84, inf, inf, inf], + [53, 93, 53, inf, inf] +]) + +all_cycles = find_all_cycles(distances) + +if all_cycles: + print("存在的回路路径为:") + for cycle in all_cycles: + print(cycle) +else: + print("不存在回路") \ No newline at end of file diff --git a/test/dfs.py b/test/dfs.py new file mode 100644 index 0000000..0909a3a --- /dev/null +++ b/test/dfs.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Time : 2023/11/3 17:43 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : dfs.py + +import numpy as np +def dfs_exists_path(distances): + num_cities = distances.shape[0] + visited = set() + start_node = 0 + + def dfs(node): + visited.add(node) + for neighbor in range(num_cities): + if neighbor != node and neighbor not in visited and np.isfinite(distances[node, neighbor]): + dfs(neighbor) + + dfs(start_node) + + return len(visited) == num_cities + + +def exists_path_without_inf(distances): + # Check if there are any infinite distances + if np.any(np.isinf(distances)): + return False + + # Set the infinite distances to a large value + max_distance = np.max(distances) + 1 + distances[np.isinf(distances)] = max_distance + + # Use DFS to check if there exists a path + return dfs_exists_path(distances) + + +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +print(exists_path_without_inf(distances)) \ No newline at end of file diff --git a/test/dfs_1.py b/test/dfs_1.py new file mode 100644 index 0000000..3507d34 --- /dev/null +++ b/test/dfs_1.py @@ -0,0 +1,53 @@ +import numpy as np + + +def dfs_tsp(graph, start, current, path, visited, cost, min_cost, min_path): + if len(path) == len(graph) and graph[current][start] != np.inf: + path.append(start) + cost += graph[current][start] + if cost < min_cost[0]: + min_cost[0] = cost + min_path[0] = path.copy() + path.pop() + cost -= graph[current][start] + return + + for next_node in range(len(graph)): + if graph[current][next_node] != np.inf and not visited[next_node]: + visited[next_node] = True + path.append(next_node) + cost += graph[current][next_node] + dfs_tsp(graph, start, next_node, path, visited, cost, min_cost, min_path) + visited[next_node] = False + path.pop() + cost -= graph[current][next_node] + + +def tsp_dfs(graph): + n = len(graph) + min_cost = [float('inf')] + min_path = [[]] + + for start_node in range(n): + visited = [False] * n + path = [start_node] + cost = 0 + visited[start_node] = True + dfs_tsp(graph, start_node, start_node, path, visited, cost, min_cost, min_path) + + return min_cost[0], min_path[0] + + +# 示例使用 +distances = np.array([ + [np.inf, 50, 99, np.inf, 64, np.inf], + [50, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +min_cost, min_path = tsp_dfs(distances) +print("Minimum cost:", min_cost) +print("Minimum path:", min_path) \ No newline at end of file diff --git a/test/greedy.py b/test/greedy.py new file mode 100644 index 0000000..9b72609 --- /dev/null +++ b/test/greedy.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Time : 2023/10/31 10:22 +# Author : lirunsheng +# User : l'r's +# Software: PyCharm +# File : greedy.py + +import numpy as np + +def tsp_greedy(distances): + num_cities = len(distances) + visited = [False] * num_cities + path = [0] # 起始城市为0 + visited[0] = True + + for _ in range(num_cities - 1): + curr_city = path[-1] + min_distance = float('inf') + next_city = None + + for city in range(num_cities): + if not visited[city] and distances[curr_city][city] < min_distance and distances[curr_city][city] != 0: + min_distance = distances[curr_city][city] + next_city = city + + if next_city is not None: + path.append(next_city) + visited[next_city] = True + if distances[path[-1]][0] != np.inf: + path.append(0) # 回到起始城市形成闭合路径 + return path + +distances = np.array([ + [np.inf, 80, 99, np.inf, 64, np.inf], + [80, np.inf, 12, 45, 74, 13], + [99, 12, np.inf, 25, np.inf, 61], + [np.inf, 45, 25, np.inf, 45, 47], + [64, 74, np.inf, 45, np.inf, np.inf], + [np.inf, 13, 61, 47, np.inf, np.inf] +]) + +path = tsp_greedy(distances) +if len(path) == 6: + print('贪心算法路径:') + print(path) +else: + print('未找到!') \ No newline at end of file