问题转化
这个问题可以转化为在一个有向图中寻找最短路径的问题。每个位置 ppp 是图中的一个节点,每次瞬移操作可以看作是节点之间的一条有向边。我们需要找到从起点 111 到终点 LLL 的最短路径。
步骤
-
问题建模:
将数轴上的每个位置 111 到 LLL 看作图中的节点。
每次瞬移操作可以从当前位置 ppp 移动到 (p+x+y−1) mod L+1(p + x + y - 1) \bmod L + 1(p+x+y−1)modL+1,其中 xxx 和 yyy 是给定集合中的元素。这形成了节点之间的有向边。 -
预处理可能的移动距离:
计算所有可能的 x+yx + yx+y 的值,并对这些值取模 LLL。这是因为在模运算中,(p+x+y) mod L(p + x + y) \bmod L(p+x+y)modL 只与 (x+y) mod L(x + y) \bmod L(x+y)modL 有关。
使用集合存储这些值,避免重复计算。 -
广度优先搜索(BFS):
BFS 是寻找无权图中最短路径的标准算法。
从起点 111 开始,逐层扩展到所有可能到达的位置,记录到达每个位置的最少步数。
当第一次到达终点 LLL 时,当前步数即为最短路径长度。
算法优化
-
预处理优化:
预处理所有可能的 (x+y) mod L(x + y) \bmod L(x+y)modL 值,将每次移动的时间复杂度从 O(n2)O (n^2)O(n2) 降低到 O(1)O (1)O(1)。 -
避免重复访问:
使用数组 distdistdist 记录到达每个位置的最少步数,同时判断是否已访问过该位置。 -
模运算处理:
计算新位置时使用 (p+x+y−1) mod L+1(p + x + y - 1) \bmod L + 1(p+x+y−1)modL+1 确保结果在 111 到 LLL 之间。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n, L;
unordered_set<int>st;
int a[2005], dist[2005];
// BFS函数,返回从起点到终点的最短路径长度,无法到达则返回-1
int bfs()
{
for (int i = 1; i <= L; i++)dist[i] = -1;
queue<int> q;
int start = 1;
dist[start] = 0;
q.push(start);
while (!q.empty())
{
int current = q.front();
q.pop();
if (current == L)return dist[current];// 检查是否到达出口
for (int s_mod_L : st)
{
int new_pos = ((current + s_mod_L - 1) % L) + 1;// 计算新位置
if (dist[new_pos] == -1)
{
dist[new_pos] = dist[current] + 1;
q.push(new_pos);
}
}
}
return -1;
}
signed main()
{
cin >> n >> L;
for (int i = 1; i <= n; i++)cin >> a[i];
for (int i = 1; i <= n; i++) // 预处理所有可能的x+y的和,并对L取模
{
for (int j = 1; j <= n; j++)
{
int s = a[i] + a[j];
st.insert(s % L);
}
}
cout << bfs();
return 0;
}