题解:P12322 [蓝桥杯 2024 国 Java C] 瞬移

问题转化

这个问题可以转化为在一个有向图中寻找最短路径的问题。每个位置 ppp 是图中的一个节点,每次瞬移操作可以看作是节点之间的一条有向边。我们需要找到从起点 111 到终点 LLL 的最短路径。

步骤

  1. 问题建模:

    将数轴上的每个位置 111LLL 看作图中的节点。
    每次瞬移操作可以从当前位置 ppp 移动到 (p+x+y−1) mod L+1(p + x + y - 1) \bmod L + 1(p+x+y1)modL+1,其中 xxxyyy 是给定集合中的元素。这形成了节点之间的有向边。

  2. 预处理可能的移动距离:

    计算所有可能的 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 有关。
    使用集合存储这些值,避免重复计算。

  3. 广度优先搜索(BFS):

    BFS 是寻找无权图中最短路径的标准算法。
    从起点 111 开始,逐层扩展到所有可能到达的位置,记录到达每个位置的最少步数。
    当第一次到达终点 LLL 时,当前步数即为最短路径长度。

算法优化

  1. 预处理优化:
    预处理所有可能的 (x+y) mod L(x + y) \bmod L(x+y)modL 值,将每次移动的时间复杂度从 O(n2)O (n^2)O(n2) 降低到 O(1)O (1)O(1)

  2. 避免重复访问:
    使用数组 distdistdist 记录到达每个位置的最少步数,同时判断是否已访问过该位置。

  3. 模运算处理:
    计算新位置时使用 (p+x+y−1) mod L+1(p + x + y - 1) \bmod L + 1(p+x+y1)modL+1 确保结果在 111LLL 之间。

代码

#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;
}    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值