似乎是个经典的套路?大概对这类数轴上哈密顿回路问题都能这么做。
我们可以将一条路径拆分成若干段来回经过相邻两条边的方案,那么大概就变成了一个类似插头DP的东西。我们考虑DP,设
F
[
i
]
[
j
]
[
k
]
F[i][j][k]
F[i][j][k]表示从左到右考虑了前
i
i
i个点,对于
[
i
−
1
,
i
]
[i-1,i]
[i−1,i]之间的边,我们从左向右经过了
j
j
j次,从右向左经过了
k
k
k次,转移的时候考虑经过
[
i
,
i
+
1
]
[i,i+1]
[i,i+1]多少次,我们发现大部分的路径都会被继承,不过我们可以新开两个插头或者合并两个插头,讨论一下就好了。
再观察一下,对于每一段区间,合法状态中的
j
−
k
j-k
j−k是定值,于是只用记录
j
j
j,复杂度就优化到了
O
(
n
2
)
\mathcal O(n^2)
O(n2)。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
typedef long long ll;
inline void update(ll &x,ll y) {
x=min(x,y);
}
ll f[2][5005];
int pos[5005],a[5005],b[5005],c[5005],d[5005];
int main() {
int n,sx,tx;
scanf("%d%d%d",&n,&sx,&tx);
for(int i=1;i<=n;i++) scanf("%d",&pos[i]);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]);
for(int i=1;i<=n;i++) scanf("%d",&d[i]);
if (sx>tx) {
swap(sx,tx);
for(int i=1;i<=n;i++) {
swap(a[i],c[i]);
swap(b[i],d[i]);
}
}
int cur=0;
memset(f[cur],0x3f,sizeof(f[cur]));
f[cur][0]=0;
for(int i=1;i<=n;i++) {
cur^=1;
memset(f[cur],0x3f,sizeof(f[cur]));
for(int j=0;j<=n;j++)
if (f[cur^1][j]<inf) {
int k=j-(i>sx&&i<=tx);
ll s=f[cur^1][j]+(ll)(j+k)*(pos[i]-pos[i-1]);
if (i>1&&!j&&!k) continue;
if (i==sx) {
update(f[cur][j+1],s+d[i]);
if (j) update(f[cur][j],s+c[i]);
}
else if (i==tx) {
update(f[cur][j-1],s+a[i]);
update(f[cur][j],s+b[i]);
}
else {
if (j) update(f[cur][j],s+a[i]+d[i]);
if (k) update(f[cur][j],s+b[i]+c[i]);
if (j&&k) update(f[cur][j-1],s+a[i]+c[i]);
update(f[cur][j+1],s+b[i]+d[i]);
}
}
}
printf("%lld\n",f[cur][0]);
return 0;
}