Part0. 前言
没想到 SPFA-SLF 冲进了最优解第一版,比多数 Dijkstra 还快。
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' | ∣i−i′∣ 时间。你需要从 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+∣i−1∣+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. 注意点
-
v v v 要判是否符合 1 ≤ v ≤ N 1 \leq v \leq N 1≤v≤N;
-
本题需开 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。