当某些点被阻挡时到达终点的最小跳跃
Posted
技术标签:
【中文标题】当某些点被阻挡时到达终点的最小跳跃【英文标题】:Minimum jumps to reach end when some points are blocked 【发布时间】:2019-12-14 15:24:35 【问题描述】:一个人目前在(0,0)
,想到达(X,0)
,他必须跳几步才能到达他的房子。从某个点说(a,0)
,他可以跳到(a + k1,0)
,即向前k1
步或他可以跳(a-k2,0)
即向后跳k2
步。他第一次跳必须是向前跳,而且他不能连续向后跳两次。但是他可以跳任意一个连续向前跳。有n个点a1,a2 upto an
他不能跳。
我必须确定到达他家的最少跳跃次数或得出他无法到达他家的结论。如果他可以到达房子打印是并指定否。如果不打印,跳转次数。
这里
X = location of persons house.
N = no. of points where he cannot jump.
k1 = forward jump.
k2 = backward jump.
例子
对于输入
X=6 N=2 k1=4 k2=2
Blocked points = 3 5
the answer is 3 (4 to 8 to 6 or 4 to 2 to 6)
用于输入
6 2 5 2
1 3
这个人无法到达他的房子
N can be upto 10^4 and X can be upto 10^5
我想过使用动态编程,但我无法实现它。有人可以帮忙吗?
【问题讨论】:
如果这些点总是0,为什么它们有y坐标? @kaya3 没关系。忽略他们 可以分享问题链接吗? 【参考方案1】:我认为您使用动态编程的方向可行,但我将展示另一种解决问题的方法,该方法具有与动态编程相同的渐近时间复杂度。
这个问题可以描述为图表中的问题,其中您将X
节点索引为1
到X
,这些是每个a
和a + k1
、b
和b - k2
之间的一条边,在此处删除 N
中的节点。
如果您可以向后跳多少次,但您不能连续跳两次,这就足够了,因此您可以添加以下修改:复制图形的节点,也复制向前的边,但让它们从复制到原来的,现在使所有向后的边缘都转到复制的图上。现在,每个后向边缘都会将您发送到重复的边缘,并且您将无法再次使用后向边缘,直到您使用前进边缘转到原始边缘。这将确保在后退边缘之后您将始终采取前进边缘 - 因此您将无法向前跳两次。
现在找到从1
到X
的最短路径就像找到最小数量的跳转,因为边是跳转。
在有向未加权图中找到最短路径需要O(|V|+|E|)
时间和内存(使用BFS
),您的图有2 * X
和|V|
,边的数量也将是2 * 2 * X
所以时间和内存O(X)
的复杂度。
如果你可以向后跳转两次,你可以使用 python 中的networkx
库进行简单的演示(你也可以使用 if 进行复杂的演示):
import matplotlib.pyplot as plt
import networkx as nx
X = 6
N = 2
k1 = 4
k2 = 2
nodes = [0, 1, 2, 4, 6]
G = nx.DiGraph()
G.add_nodes_from(nodes)
for n in nodes:
if n + k1 in nodes:
G.add_edge(n, n + k1)
if n - k2 in nodes:
G.add_edge(n, n - k2)
nx.draw(G, with_labels=True, font_weight='bold')
plt.plot()
plt.show()
path = nx.shortest_path(G, 0, X)
print(f"Number of jumps: len(path) - 1. path: str(path)")
【讨论】:
这个答案是否考虑了 k1=3、k2=4、X=2 之类的示例,其中解决方案可能是先回到 -1,然后再回到 2? 没有@גלעדברקן,但假设k1
和k2
小于X
和N
只包含0
和X
之间的节点,你可以从@987654346添加节点左右。
这个答案如何考虑限制,“不能连续向后跳两次?”
@Yonlif 我如何复制节点?你能在 c++ 或 c 中添加一个实现吗?
@Klasen 您可能应该使用 גלעדברקן 的代码。如果你愿意,我可以用 C++ 编写。【参考方案2】:
广度优先搜索是否足够有效?
这样的? (Python代码)
from collections import deque
def f(x, k1, k2, blocked):
queue = deque([(k1, 0, None, None)])
while (queue):
(p, depth, direction, prev) = queue.popleft()
if p in blocked or (x + k2 < p < x - k1): # not sure about these boundaries ... ideas welcome
continue
if p == x:
return depth
blocked.add(p) # visited
queue.append((p + k1, depth + 1, "left", direction))
if prev != "right":
queue.append((p - k2, depth + 1, "right", direction))
X = 6
k1 = 4
k2 = 2
blocked = set([3, 5])
print f(X, k1, k2, blocked)
X = 2
k1 = 3
k2 = 4
blocked = set()
print f(X, k1, k2, blocked)
【讨论】:
【参考方案3】:这里是 גלעדברקן 在 c++ 中的代码:
#include <iostream>
#include <queue>
using namespace std;
struct node
int id;
int depth;
int direction; // 1 is left, 0 is right
;
int BFS(int start, int end, int k1, int k2, bool blocked[], int length)
queue<node> q;
blocked[0] = true;
q.push(start, 0, 0);
while(!q.empty())
node f = q.front();
q.pop();
if (f.id == end)
return f.depth;
if(f.id + k1 < length and !blocked[f.id + k1])
blocked[f.id + k1] = true;
q.push(f.id + k1, f.depth + 1, 0);
if (f.direction != 1) // If you just went left - don't go left again
if(f.id - k2 >= 0 and !blocked[f.id - k2])
blocked[f.id - k2] = true;
q.push(f.id - k2, f.depth + 1, 1);
return -1;
int main()
bool blocked[] = false, false, false, false, false, false, false;
std::cout << BFS(0, 6, 4, 2, blocked, 7) << std::endl;
return 0;
您可以控制步骤的长度、开始和结束以及阻塞的节点。
【讨论】:
以上是关于当某些点被阻挡时到达终点的最小跳跃的主要内容,如果未能解决你的问题,请参考以下文章