codeforce Gym 100570B ShortestPath Query (最短路SPFA)

题意:询问单源最短路径,每条边有一个颜色,要求路径上相邻边的颜色不能相同,无重边且边权为正。

题解:因为路径的合法性和边的颜色有关,

所以在做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\n",d2[t]!=INF?d2[t]:-1);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/jerryRey/p/4687777.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值