在下班后的最后几天,我正在尝试将Euler项目作为学习Python的一种练习,现在是Problem 18
我研究了这个问题,并认为可以通过使用Dijkstra的算法来解决,将节点的值设为负整数,从而找到“最长”的路径。
我的解决方案似乎几乎是正确的(我得到1068)-这是错误的。它打印出一条路径,但据我所知,这不是正确的路径。但是从一段时间来看,我不知道为什么。
也许这个问题不能用我的方法解决,我还需要其他方法,例如动态编程-还是我对Dijkstra的实现是错误的?
我非常有信心从文件到图形的解析按预期进行。
这是数据集:
75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23
这是代码。只要上面内容的文件路径正确,它就是一个完全的“工作示例”。
class Graph:
def __init__(self):
self.nodes = []
self.edges = []
def add_node(self, node):
self.nodes.append(node)
def add_edge(self, edge):
self.edges.append(edge)
def edges_to_node(self, n):
edges = [edge for edge in self.edges if edge.node1.id == n.id]
return edges
class Node:
def __init__(self, id, value, goal):
self.id = id
self.value = value
self.goal = goal
self.visited = False
self.distance = 10000
self.previous = None
def __str__(self):
return "{} - {}".format(self.value, self.goal)
def __repr__(self):
return "{} - {}".format(self.value, self.goal)
class Edge:
def __init__(self, node1, node2):
self.node1 = node1
self.node2 = node2
f = open("problem18.data", "r")
content = f.read()
lines = content.split("\n")
data = []
graph = Graph()
index_generator = 1
last_line = len(lines) - 1
for i in range(len(lines)):
data.append([])
numbers = lines[i].split()
for number in numbers:
goal = i == last_line
data[-1].append(Node(index_generator, -int(number), goal))
index_generator += 1
for i in range(len(data)):
for j in range(len(data[i])):
node = data[i][j]
graph.add_node(node)
if i != last_line:
node2 = data[i+1][j]
node3 = data[i+1][j+1]
edge1 = Edge(node, node2)
edge2 = Edge(node, node3)
graph.add_edge(edge1)
graph.add_edge(edge2)
def dijkstra(graph, start):
start.distance = 0
queue = [start]
while len(queue):
queue.sort(key=lambda x: x.value, reverse=True)
current = queue.pop()
current.visited = True
if current.goal:
return reconstrcut_path(start, current)
edges = graph.edges_to_node(current)
for edge in edges:
neighbour = edge.node2
if neighbour.visited:
continue
queue.append(neighbour)
new_distance = current.distance + neighbour.value
if new_distance < neighbour.distance:
neighbour.distance = new_distance
neighbour.previous = current
return []
def reconstrcut_path(start, n):
path = []
current = n
while current.id is not start.id:
path.append(current)
current = current.previous
path.append(start)
return path
path = dijkstra(graph, graph.nodes[0])
tally = 0
for node in path:
number = max(node.value, -node.value)
print(number)
tally += number
print(tally)
您能帮我解决此解决方案的问题吗?
编辑:运行的控制台输出:
98
67
91
73
43
47
83
28
73
75
82
87
82
64
75
1068
答案 0 :(得分:4)
实际上,动态编程会巧妙地解决这一问题。我对此问题67的解决方案少于20行。
这里的重点是Dijkstra方法:沿三角形向下移动,在每个节点上保持最大路径开销。第1行很简单:
75
第2行类似,因为两个值都是结尾:每个值只有一条可能的路径:
95+75 64+75
计算结果为
170 139
第3行有两个端点,但是中间值给了我们关键的逻辑:保持两条路径中的较大者:
17+170 47+max(170, 139) 82+139
187 217 221
第4行有两个中间点...继续进行下去:
18+187 35+max(187, 217) 87+max(217, 221) 10+221
205 252 308 231
你能从这里拿走吗?
作为对您的核对,正确的答案与您最初获得的答案非常接近。
您的解决方案失败,因为您未应用Dijkstra的算法。这要求您保持在搜索中到达每个节点的最佳路径。取而代之的是,您使用了逐行的贪婪算法:在整个过程中,到目前为止只保留了最佳路径。
特别是,当您在底部行的右侧附近找到98
时,就强行假设它是最佳路径的一部分。您逐行继续此操作。专门配置数据集以使此方法失败。最佳路径始于93 + 73 + 58
序列。
您必须牢记所有路径;有一条路径不是最下面两行的最佳总和,但追赶到中间的行,而“胖”路径却饿死了中间的一些较低数字。
答案 1 :(得分:1)
考虑以下备用数据集:
01
00 01
00 00 01
00 00 00 01
99 00 00 00 01
至少在成本为零的情况下,Dijkstra会探索1的路径和“正好在路径之外”的零,但除此之外别无其他。沿1s路径再走一步的节点始终是队列中的最佳节点,并且以目标节点结束,因此算法在不探索三角形其余部分的情况下终止。甚至看不到左下角有一个99。