題意
給出一個nm的無向圖,有邊權。
張三在起點s,目標是t。
點分成左點右點和中間點,到左點的時候必須用左手,到右點的時候必須用右手,中間點沒有特殊要求。
張三每次切換左右手都要花費額外的時間,詢問起點到終點的最短路。
題解
把每個點拆成兩個點,左點拆成兩個左點,右點拆成兩個右點,中間點拆成一左一右兩個點,然后跑dijsktra。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=4e5+10; const ll inf=1e18; int n,m,s,t,cost; string ss; struct node { int u; int v; int w; int next; }edge[maxn*8]; int head[maxn*3]; int tol=0; void addedge (int u,int v,int w) { edge[tol].u=u; edge[tol].v=v; edge[tol].w=w; edge[tol].next=head[u]; head[u]=tol++; edge[tol].u=v; edge[tol].v=u; edge[tol].w=w; edge[tol].next=head[v]; head[v]=tol++; } ll d[maxn]; int visit[maxn]; char wjm[maxn]; struct qnode { int v; ll w; bool operator < (const qnode &r) const { return w>r.w; } }; void dij (int s) { memset(visit,0,sizeof(visit)); for (int i=1;i<=2*n;i++) d[i]=inf; priority_queue<qnode> q; d[s]=0; q.push({s,0}); qnode tmp; while (!q.empty()) { tmp=q.top(); q.pop(); int u=tmp.v; if (visit[u]) continue; visit[u]=1; for (int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].v; int tt; if (wjm[u]==wjm[v]) tt=0; else tt=1; if (!visit[v]&&d[v]>d[u]+edge[i].w+tt*cost) { d[v]=d[u]+edge[i].w+tt*cost; //if (!visit[v]) { q.push({v,d[v]}); //visit[v]=1; //} } } } } int main () { int T; scanf("%d",&T); while (T--) { scanf("%d%d%d%d%d",&n,&m,&s,&t,&cost); for (int i=1;i<=n*2;i++) head[i]=-1; cin>>ss; //每個點拆成兩個點 //l點拆成兩個L //R點拆成兩個R //M點拆成一個L一個R for (int i=1;i<=n;i++) { if (ss[i-1]=='L') { wjm[i]='L'; wjm[i+n]='L'; } else if (ss[i-1]=='R') { wjm[i]='R'; wjm[i+n]='R'; } else { wjm[i]='L'; wjm[i+n]='R'; } } tol=0; for (int i=0;i<m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(u+n,v,w); addedge(u+n,v+n,w); addedge(u,v+n,w); } ll ans=1e18; dij(s); ans=min(ans,d[t]); ans=min(ans,d[t+n]); dij(s+n); ans=min(ans,d[t]); ans=min(ans,d[t+n]); printf("%lld\n",ans); } return 0; }