P10948 升降梯上 灰 题解

Part0. 前言

没想到 SPFA-SLF 冲进了最优解第一版,比多数 Dijkstra 还快。

评测记录(SPFA-SLF 43ms)

评测记录(Dijkstra 44ms)


Part1. 题意简述

M M M 个移动系数 − N < C 1 < C 2 < . . . < C M < N -N < C_1 < C_2 < ... < C_M < N N<C1<C2<...<CM<N,若当前使用移动系数 C i C_i Ci,当前位置为 P P P,则移动一次将移至 P + C i P + C_i P+Ci,耗费 2 × ∣ C i ∣ 2 \times | C_i | 2×Ci;若要换为使用移动系数 C i ′ C_{i'} Ci,则额外消耗 ∣ i − i ′ ∣ | i - i' | ii 时间。你需要从 1 1 1 号点移动至 N N N 号点,求最短消耗的时间。


Part2. 思路

显然,这题是道最短路。

d i s u , l dis_{u,l} disu,l 表示从 1 1 1 出发移至 u u u 点,当前的移动系数为 C l C_l Cl 的最少耗费时间。

我们考虑如何转移。每枚举到一个点,我们枚举本次移动的移动系数,并计算其本次移动之后的状态。设其当前在 u u u 位置,当前移动系数为 C l C_l Cl,本次移动后在 v v v 位置,本次移动的移动系数为 C i C_i Ci,则转移方程可设为:

d i s v , i = min ⁡ ( d i s v , i , d i s u , l + ∣ i − 1 ∣ + 2 × ∣ C i ∣ ) dis_{v,i} = \min ( dis_{v,i} , dis_{u,l} + | i - 1 | + 2 \times | C_i |) disv,i=min(disv,i,disu,l+i1∣+2×Ci)

最后输出 max ⁡ i = 1 m d i s n , i \max \limits_{i=1}^{m} dis_{n,i} i=1maxmdisn,i

最短路我用了 SPFA-SLF 与 Dijkstra 两种算法,读者可自行研究更快的做法。


Part3. 注意点

  1. v v v 要判是否符合 1 ≤ v ≤ N 1 \leq v \leq N 1vN

  2. 本题需开 long long,不开 90pts。


Part4. 代码

#include <bits/stdc++.h>
using namespace std;
#define int long long

#define pii pair<int,int>
#define fi first
#define se second

int n,m,s,c[30],dis[1010][30];
bool vis[1010][30];

void spfa () {
    memset (dis,0x3f,sizeof (dis));
    memset (vis,1,sizeof (vis));
    deque<pii> q;
    dis[1][s]=0;
    vis[1][s]=0;
    q.push_front ({1,s});
    while (q.size ()) {
        pii p=q.front ();
        q.pop_front ();
        int u=p.fi,l=p.se;
        vis[u][l]=1;
        for (int i=1;i<=m;i++) {
            int v=u+c[i],w=abs (i-l)+2*abs (c[i]);
            if (v<1||v>n) continue;
            if (dis[v][i]>dis[u][l]+w) {
                dis[v][i]=dis[u][l]+w;
                if (vis[v][i]) {
                    if (q.empty ()||dis[v][i]<dis[q.front ().fi][q.front ().se]) q.push_front ({v,i});
                    else q.push_back ({v,i});
                    vis[v][i]=0;
                }
            }
        }
    }
}

struct tup {
    int u,l,w;
    bool operator < (const tup &x) const {
        return w>x.w;
    }
};

void dijkstra () {
    memset (dis,0x3f,sizeof (dis));
    priority_queue<tup> q;
    q.push ({1,s,dis[1][s]=0});
    while (q.size ()) {
        tup p=q.top ();
        q.pop ();
        int u=p.u,l=p.l;
        if (vis[u][l]) continue;
        vis[u][l]=1;
        for (int i=1;i<=m;i++) {
            int v=u+c[i],w=abs (i-l)+2*abs (c[i]);
            if (v<1||v>n) continue;
            if (dis[v][i]>dis[u][l]+w) {
                dis[v][i]=dis[u][l]+w;
                q.push ({v,i,dis[v][i]});
            }
        }
    }
}

signed main () {
    cin>> n>> m;
    for (int i=1;i<=m;i++) {
        cin>> c[i];
        if (!c[i]) s=i;
    }
    spfa ();
    //dijkstra ();
    //上两行为不同算法,可改注释位置测试
    int ans=112890981491;
    for (int i=1;i<=m;i++)
        ans=min (ans,dis[n][i]);
    cout<< (ans==112890981491? -1: ans);
    return 0;
}

杜绝抄袭,养成独立思考的好习惯。


Part.5 后记

SPFA-SLF 虽然这题跑得快,但是所有 SPFA 的优化形式都会被毒瘤特殊数据卡掉,所以建议少用 SPFA 多用 Dijkstra。


Part6. 推荐习题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值