是 @Yubai 的思路
发现一个点还可以被倒着经过一次就很烦
可以另外建出一类全是反向的边,这样就变成了从同一起点出发,到达同一终点的最短路
然而这两个几乎不是一个图,考虑二维spfa
令 \(dis[i][j]\) 为正向走到 \(i\) ,逆向走到 \(j\) 的最短长度
再令 \(rec[i][j]\) 为正向走到 \(i\) ,逆向走到 \(j\) 的最短路经过的点集
二维spfa跑一遍就行了
至于有两个状态同时转移到了一个点且dis相同的情况该保留谁的rec
随便选一个就行,它确实不会产生影响
考虑我们构造一组数据来卡它的过程
我们会试着构造一种情况使它在从n往回走的时候经过仅有其中一个点集里有的点
然而我们DP定义是什么?正向走到 \(i\) ,逆向走到 \(j\)
我们已经把逆向走转化为正向走了,也就消除了后效性
所以没有影响
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 255
#define ll long long
#define reg register int
#define fir first
#define sec second
#define make make_pair
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
int head[N], rhead[N], size, val[N], dis[N];
bool vis[N], could[N];
struct edge{int to, next;}e[N*N*2];
inline void add1(int s, int t) {e[++size].to=t; e[size].next=head[s]; head[s]=size;}
inline void add2(int s, int t) {e[++size].to=t; e[size].next=rhead[s]; rhead[s]=size;}
void spfa(int s) {
memset(dis, 127, sizeof(int)*(n+1));
dis[s]=0;
queue<int> q;
q.push(s);
int u;
while (q.size()) {
u=q.front(); q.pop();
vis[u]=0;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (!could[v]) continue;
if (dis[v]>dis[u]) {
dis[v]=dis[u];
if (!vis[v]) q.push(v), vis[v]=1;
}
}
}
}
namespace force{
void solve() {
int lim=1<<n, sum, ans=INF;
for (reg s=1; s<lim; ++s) {
if (!(s&1) || !(s&(1<<(n-1)))) continue;
sum=0;
memset(could, 0, sizeof(could));
for (reg i=0; i<n; ++i) if (s&(1<<i)) sum+=val[i+1], could[i+1]=1;
if (sum>=ans) goto jump;
spfa(1); if (dis[n]) goto jump;
spfa(n); if (dis[1]) goto jump;
ans = min(ans, sum);
jump: ;
}
printf("%d\n", ans==INF?-1:ans);
exit(0);
}
}
namespace task1{
void solve() {
srand(time(0));
int sum, ans=INF;
int buc[N];
for (int i=1; i<=n; ++i) buc[i]=i;
for (int T=1,num; ; ++T) {
if ((!(T%10))&&clock()>850000) break;
memset(could, 0, sizeof(bool)*(n+1));
random_shuffle(buc+1, buc+n+1);
num=rand()%n+1;
could[1]=could[n]=1;
sum=val[1]+val[n];
for (reg i=1; i<=num; ++i)
if (buc[i]!=1 && buc[i]!=n)
sum+=val[buc[i]], could[buc[i]]=1;
if (sum>=ans) goto jump2;
spfa(1); if (dis[n]) goto jump2;
spfa(n); if (dis[1]) goto jump2;
ans = min(ans, sum);
jump2: ;
}
printf("%d\n", ans==INF?-1:ans);
exit(0);
}
}
namespace task{
int dis[N][N];
bool vis[N][N];
bitset<256> rec[N][N];
void spfa(int s) {
memset(dis, 127, sizeof(dis));
dis[s][s]=val[s]; rec[s][s][s]=1;
queue< pair<int, int> > q;
q.push(make(s, s));
pair<int, int> u;
while (q.size()) {
u=q.front(); q.pop();
//cout<<"u: "<<u.fir<<' '<<u.sec<<endl;
vis[u.fir][u.sec]=0;
for (int i=head[u.fir],v,sum; ~i; i=e[i].next) {
v = e[i].to;
//cout<<"v: "<<v<<endl;
sum=dis[u.fir][u.sec]+(rec[u.fir][u.sec][v]?0:val[v]);
if (dis[v][u.sec] > sum) {
dis[v][u.sec]=sum; rec[v][u.sec]=rec[u.fir][u.sec];
rec[v][u.sec][v]=1;
if (!vis[v][u.sec]) q.push(make(v, u.sec)), vis[v][u.sec]=1;
}
}
for (int i=rhead[u.sec],v,sum; ~i; i=e[i].next) {
v = e[i].to;
//cout<<"v: "<<v<<endl;
sum=dis[u.fir][u.sec]+(rec[u.fir][u.sec][v]?0:val[v]);
if (dis[u.fir][v] > sum) {
dis[u.fir][v]=sum; rec[u.fir][v]=rec[u.fir][u.sec];
rec[u.fir][v][v]=1;
if (!vis[u.fir][v]) q.push(make(u.fir, v)), vis[u.fir][v]=1;
}
}
}
}
void solve() {
spfa(1);
printf("%d\n", dis[n][n]==2139062143?-1:dis[n][n]);
exit(0);
}
}
signed main()
{
memset(head, -1, sizeof(head));
memset(rhead, -1, sizeof(rhead));
n=read(); m=read();
if (!m) {puts("-1"); return 0;}
for (int i=1; i<=n; ++i) val[i]=read();
for (int i=1,u,v; i<=m; ++i) {u=read(); v=read(); add1(u, v); add2(v, u);}
//if (n<=20) force::solve();
//else task1::solve();
task::solve();
return 0;
}