题目
题目连接
php
题目大意是:
给定一张无向有权图,已知其起点和终点,每一个节点有方向LR或M。蛋糕每次换手须要必定时间x。在方向为L或R的节点,蛋糕必须在对应的手上,而方向为M的地方则无所谓。
ios
问最终从起点到终点的最短路程是多少。url
拆点+最短路
这道题,一步一步的来分析spa
第一步,若是没有对点的方向限制,将会是一个很是简单的最短路问题。如今有了方向限制。.net
第二步,假设当前仅有左右两种方向的限制。那么问题也会变得简单只须要将两端方向不同的边权加上x,这样就没有了方向限制,跑第一步便可。code
第三步,考虑到存在方向为M的点,能够将其看做是两个点,一个是方向为左的点一个是方向为右的点,将其拆开,与周围的点从新建边。blog
通过这样一顿操做,图中就没有方向为M的点了,剩下的图就只剩下方向为左或者右的点了,按照刚刚第二步分析的那样实现就好。
图片
须要注意的是,这里拆点可能会形成边的数量不少,建议开10被的m。ci
另外,起点和终点有可能被拆成两个顶点,为了避免跑屡次最短路,能够建造一个虚拟起点和终点分别向拆开的起点和终点链接长度为0的边。get
代码:
#include<iostream> #include<algorithm> #include<queue> using namespace std; const int M = 1000060; const int N = 1000060; struct E{ int point; int next; long long dis; }nxt[M]; struct Node{ int po; long long dis; bool operator < (const Node& o)const{ return dis > o.dis; } }; priority_queue<Node> q; int head[N]; int dir[N]; int T,n,m,s1,s2,t1,t2,x,tot,s,t; char dirC[N]; bool vis[N]; void link(int a,int b,long long dis){ nxt[++tot] = {b,head[a],dis}; head[a] = tot; } void dfs1(int k){ vis[k] = true; if(dir[k] == 0){ n++; vis[n] = true; dir[k] = -1; dir[n] = 1; if(k == s1)s2 = n; if(k == t1)t2 = n; for(int i = head[k],j;i;i = nxt[i].next){ j = nxt[i].point; link(j,n,nxt[i].dis); link(n,j,nxt[i].dis); } } for(int i = head[k],j;i;i = nxt[i].next){ if(vis[nxt[i].point]) continue; dfs1(nxt[i].point); } } void dfs2(int k){ vis[k] = true; for(int i = head[k];i;i = nxt[i].next){ nxt[i].dis += (dir[k] != dir[nxt[i].point]) * x; } for(int i = head[k];i;i = nxt[i].next){ if(vis[nxt[i].point]) continue; dfs2(nxt[i].point); } } void clearVis(){ for(int i = 1;i <= n;i++){ vis[i] = false; } } long long dijkstar(){ clearVis(); q.push({s,0}); Node o; while(!q.empty()){ o = q.top(); q.pop(); if(vis[o.po]) continue; if(o.po == t) return o.dis; vis[o.po] = true; for(int i = head[o.po],j;i;i = nxt[i].next){ j = nxt[i].point; if(vis[j]) continue; q.push({j,o.dis + nxt[i].dis}); } } } int main(){ for(cin >> T;T;T--){ tot = 0;s2 = t2 = 0; t = s = 0; while(!q.empty()) q.pop(); scanf("%d%d%d%d%d",&n,&m,&s1,&t1,&x); scanf("%s",dirC + 1); for(int i = 1;i <= n;i++){ if(dirC[i] == 'R') dir[i] = 1; if(dirC[i] == 'L') dir[i] = -1; if(dirC[i] == 'M') dir[i] = 0; } for(int i = 1;i <= n << 1;i++) head[i] = 0; for(int i = 1,a,b,dis;i <= m;i++){ scanf("%d%d%d",&a,&b,&dis); link(a,b,dis); link(b,a,dis); } clearVis(); dfs1(s1); s = ++n;t = ++n; clearVis(); dfs2(s1); if(s2) dfs2(s2); link(s,s1,0); if(s2) link(s,s2,0); link(t1,t,0); if(t2) link(t2,t,0); cout << dijkstar() << endl; } }