Dijkstra

老是感觉自己有一种心有余而力不足的感觉,一个简单的Dijkstra都花了我一天的时间去学习,有好多时候都是明明看的懂解释,却没有办法实现,总地有参照才可以写出来,

是不是 因为做题太少,对题解有依赖,都无从得知。写博客也只是在借鉴别人,把自己所做过的题 ,所有的思路都记录下来,在参加比赛之前 有一个复习有一个回顾,也可能是无用功吧,不管是会不会有效吧,做了总比没做强,希望借此改变现状 。

Dijkstra

void dijkstra(int s){//优先级队列的作用:把所有的点分为四种:1用过了的2正在用 的3与正在用有关的点4不符合的。这样 每次会把与正在用的点相关的边放进去,并不断地更新最短路实现遍历所有的边并找出最短路。
    int i,j,k,pr=s;
    for(i=1;i<=n;i++)d[i]=inf;
    priority_queue<qnode> q;
    d[pr]=0;
    q.push((qnode){pr,0});
    path[pr]=1;//记录路径数
    while(!q.empty()){
        qnode u=q.top();
        q.pop();
        if(b[u.v])continue;
        b[pr=u.v]=1;
        for(j=head[pr];j;j=e[j].next){
            k=e[j].to;
            if(d[pr]+e[j].w==d[k])
                path[k]=(path[k]+path[pr])%M;//如果有多条最短路用此来记录看其是否为唯一的,又因为可能爆ll所以取模
            else if(d[pr]+e[j].w<d[k]){//第一次的时候因为都是INF所每一个都会被加进que中,所以就是遍历了所有的路,并且只记录了所有的最短路并没有记录其他的。
                d[k]=d[pr]+e[j].w;
                q.push((qnode){k,d[k]});
                path[k]=path[pr];
            }
        }
    }
}


例题 :cf 567E

题意是总统要回家,让你找出最短路,如果现有的最短路必须要用到某一条边,就输出YES。再如果是一定不会走的边如图

A点的那条边就一定不会通过所以就输出NO。剩下的就输出每条边需要减少多少权值才可以成为最短路。

题解:先正者一边dijkstra在反着一遍,这样就可以用 d[0][u]+d[1][v]+W-min+1来算出所需要修的 最小值(0和1表示正反)

并且在过程中记录路径数,若path[0][u]*path[1][v]==path[0][t];判断该路是不是必须通过的。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 100005
#define M 5462617
#define inf 0x3f3f3f3f3f3f3f3fll
#define ll long long
#define add(u,v,w) e[++cnt]=(edge){v,head[0][u],w};head[0][u]=cnt;e[++cnt]=(edge){u,head[1][v],w};head[1][v]=cnt
using namespace std;
struct edge{
    ll to,next,w;
}e[N<<1];
struct road{
    ll u,v,w;
}l[N];
struct qnode{
    ll v,c;
    bool operator <(const qnode &r)const{
        return c>r.c;
    }
};
ll n,m,s,t,u,v,w,d[2][N],cnt,head[2][N],path[2][N];
bool b[2][N];
void dijkstra(int f){
    int i,j,k,pr=f?t:s;
    for(i=1;i<=n;i++)d[f][i]=inf;
    priority_queue<qnode> q;
    d[f][pr]=0;
    q.push((qnode){pr,0});
    path[f][pr]=1;
    while(!q.empty()){
        qnode u=q.top();
        q.pop();
        if(b[f][u.v])continue;
        b[f][pr=u.v]=1;
        for(j=head[f][pr];j;j=e[j].next){
            k=e[j].to;
            if(d[f][pr]+e[j].w==d[f][k])
                path[f][k]=(path[f][k]+path[f][pr])%M;
            else if(d[f][pr]+e[j].w<d[f][k]){
                d[f][k]=d[f][pr]+e[j].w;
                q.push((qnode){k,d[f][k]});
                path[f][k]=path[f][pr];
            }
        }
    }
}
int main() {
    int i,j;
    cin>>n>>m>>s>>t;
    for(i=1;i<=m;i++){
        scanf("%lld%lld%lld",&u,&v,&w);
        add(u,v,w);
        l[i]=(road){u,v,w};
    }
    dijkstra(0);
    dijkstra(1);
    for(i=1;i<=m;i++)
    {
        u=l[i].u;
        v=l[i].v;
        w=l[i].w;
        if(d[0][u]+d[1][v]+w==d[0][t]){
            if(path[0][u]*path[1][v]%M==path[0][t])
                puts("YES");
            else if(w>1)
                puts("CAN 1");
            else puts("NO");
        }
        else if(d[0][u]+d[1][v]+1<d[0][t])
            printf("CAN %lld\n",d[0][u]+d[1][v]+w-d[0][t]+1);
        else
            puts("NO");
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值