题意:询问单源最短路径,每条边有一个颜色,要求路径上相邻边的颜色不能相同,无重边且边权为正。
题解:因为路径的合法性和边的颜色有关,
所以在做spfa的时候,把边丢到队列中去,松弛的时候注意判断一下颜色,d数组表示到这条边的出点v的距离。
期望复杂度是O(km),k是边入队次数,m是边数。最后根据边来松弛顶点,O(m),总复杂度是O(km+m)。
一开始想的Dijkstra(看到边权为正。。),存点和之前边的颜色每次更新的时候判断来的那个点的颜色和当前边的颜色是否一样,WA了,很快我就意识到,入点不能只保存最短路径的颜色c1,如果边的颜色和c1一样,那么会判成路径不合法,但是实际上可能还存在一条次短路径且颜色和c1不等。因此每个点只需保存两个信息。
#include<cstdio> #include <queue> #include<cstring> using namespace std; typedef long long ll; #define mins(s,v) if(s>v) s = v const int maxn = 1e5+4; const ll INF = 0x7f7f7f7f7f7f7f7fLL; int head[maxn],nxt[maxn],to[maxn],col[maxn],wei[maxn]; int ecnt; ll d[maxn];//edge ll d2[maxn];//vex int n,m,C,q; int s,t; bool vis[maxn]; void spfa() { memset(d,0x7f,sizeof(ll)*m); memset(vis,0,sizeof(vis)); queue<int> q; for(int i = head[s]; ~i ; i = nxt[i] ){ q.push(i); vis[i] = true; d[i] = wei[i]; } while(q.size()){ int e = q.front(); q.pop(); vis[e] = false; for(int i = head[to[e]]; ~i; i = nxt[i]) { if(col[e] != col[i] && wei[i]+d[e] < d[i] ){ d[i] = wei[i] + d[e]; if(!vis[i]) { q.push(i); vis[i] = true; } } } } memset(d2+1,0x7f,sizeof(ll)*n); d2[s] = 0; for(int i = 0; i < m; i++) mins(d2[to[i]],d[i]); } inline void AddEdge(int u,int v,int w,int c) { to[ecnt] = v; wei[ecnt] = w; col[ecnt] = c; nxt[ecnt] = head[u]; head[u] = ecnt++; } int main() { scanf("%d%d%d",&n,&m,&C); memset(head+1,-1,sizeof(int)*n); for(int i = 0; i < m; i++){ int u,v,c,w; scanf("%d%d%d%d",&u,&v,&w,&c); AddEdge(u,v,w,c); } scanf("%d%d",&s,&q); spfa(); for(int i = 0; i < q; i++){ scanf("%d",&t); printf("%I64d ",d2[t]!=INF?d2[t]:-1); } return 0; }