道路和航线,题解

Posted pl_er

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了道路和航线,题解相关的知识,希望对你有一定的参考价值。

题目:

题意:

  题目说的简洁明了,有两种路,一种没负数,一种没环,求单元最短路。

分析:

  spfa随便优化(双端队列)一下水过。。。

  当然这好像并不是正解。。。

  其实看到这一题,相信大家都能想到类似缩点的做法,有很明显的暗示,所有我们直接考虑将双向边所连的点缩成一个,然后就是个有向无环图,然后再跑非常好想到拓扑排序,然后没有入度之后就可以安心的跑dij去贪心了。

  说一下,有同学说:我直接给所有边加上一个较大的数字,然后最后再减去,跑dij就好了。。。注意dij的贪心思想是怎么来的,这样。。。显然不好。

  又有人说,你这样处理不还是会有负权吗。。。

  还是要弄明白dij的贪心思想怎么来的,没有负权边就好啦,dis可以是负数。

  最后就是两个代码。

  

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=25000+10;
struct E{
    int to;
    int next;
    int val;
    E(){
        val=to=next=0;
    }
}ed[maxn*6];
int head[maxn];
int tot;
deque<int> q;
int vis[maxn];
int dis[maxn];
void J(int a,int b,int c){
    tot++;
    ed[tot].to=b;
    ed[tot].val=c;
    ed[tot].next=head[a];
    head[a]=tot;
}
int main(){
    int T,R,P,S;
    memset(dis,0x3f,sizeof(dis));
    scanf("%d%d%d%d",&T,&R,&P,&S);
    int js1,js2,js3;
    for(int i=1;i<=R;i++){
        scanf("%d%d%d",&js1,&js2,&js3);
        J(js1,js2,js3);
        J(js2,js1,js3);
    }
    for(int i=1;i<=P;i++){
        scanf("%d%d%d",&js1,&js2,&js3);
        J(js1,js2,js3);
    }
    vis[S]=1;
    q.push_back(S);
    dis[S]=0;
    while(!q.empty()){//直接spfa
        int s=q.front();
        q.pop_front();
        vis[s]=0;
        for(int i=head[s];i;i=ed[i].next){
            if(vis[ed[i].to])
                dis[ed[i].to]=min(dis[ed[i].to],dis[s]+ed[i].val);
            else{
                if(dis[ed[i].to]>dis[s]+ed[i].val){
                    dis[ed[i].to]=dis[s]+ed[i].val;
                    vis[ed[i].to]=1;
                    if(q.empty())//注意判空
                        q.push_back(ed[i].to);
                    else if(dis[ed[i].to]>=dis[q.front()])//小小优化
                        q.push_back(ed[i].to);
                    else
                        q.push_front(ed[i].to);
                }
            }
        }
    }
    for(int i=1;i<=T;i++)
        if(dis[i]>=1e9)
            printf("NO PATH\\n");
        else
            printf("%d\\n",dis[i]);
}
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int maxn=25000+10;
struct E{
    int to;
    int next;
    bool x;//记录是什么样的边
    int val;
    E(){
        to=next=x=val=0;
    }
}ed[maxn*6];
int head[maxn];
int tot;//
void J(int a,int b,int c,bool d){
    tot++;
    ed[tot].to=b;
    ed[tot].val=c;
    ed[tot].x=d;
    ed[tot].next=head[a];
    head[a]=tot;
}
int js;//记录缩成的点数
int bel[maxn];//记录点属于哪个缩之后的点
int rd[maxn];//记录缩之后的点的入度
int vis[maxn];//dij用的数组
vector<int> ve[maxn];//记录每个缩之后的点所包含的点
void Dfs(int x){//缩点
    ve[js].push_back(x);
    bel[x]=js;
    for(int i=head[x];i;i=ed[i].next)
        if(!bel[ed[i].to])
            Dfs(ed[i].to);
}
struct Node{
    int dis;
    int x;
    Node(){
    }
    Node(int a,int b){
        dis=a;
        x=b;
    }
    friend bool operator < (Node a,Node b){
        return a.dis>b.dis;
    }
};
queue<int> qu;//拓扑排序用
priority_queue<Node> que[maxn];//dij用
int dis[maxn];//dij用
void Dij(int x){
    while(!que[x].empty()){
        Node js=que[x].top();
        que[x].pop();
        if(vis[js.x])
            continue;
        vis[js.x]=1;
        for(int i=head[js.x];i;i=ed[i].next){
            if(!ed[i].x)//注意特判
                continue;
            if(dis[ed[i].to]>dis[js.x]+ed[i].val){
                dis[ed[i].to]=dis[js.x]+ed[i].val;
                que[x].push(Node(dis[ed[i].to],ed[i].to));
            }
        }
    }
}
int main(){
    int T,R,P,S;
    scanf("%d%d%d%d",&T,&R,&P,&S);
    int js1,js2,js3;
    for(int i=1;i<=R;i++){
        scanf("%d%d%d",&js1,&js2,&js3);
        J(js1,js2,js3,1);
        J(js2,js1,js3,1);
    }
    for(int i=1;i<=T;i++)//缩点
        if(!bel[i]){
            js++;
            Dfs(i);
        }
    for(int i=1;i<=P;i++){
        scanf("%d%d%d",&js1,&js2,&js3);
        J(js1,js2,js3,0);
        rd[bel[js2]]++;
    }
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=js;i++)
        if(rd[i]==0)
            qu.push(i);
    dis[S]=0;
    que[bel[S]].push(Node(0,S));
    while(!qu.empty()){
        int s=qu.front();
        qu.pop();
        Dij(s);
        for(int i=0;i<ve[s].size();i++)
            for(int j=head[ve[s].at(i)];j;j=ed[j].next){//这里可以不特判
                rd[bel[ed[j].to]]--;
                if(dis[ve[s].at(i)]+ed[j].val<dis[ed[j].to]){
                    dis[ed[j].to]=dis[ve[s].at(i)]+ed[j].val;
                    que[bel[ed[j].to]].push(Node(dis[ed[j].to],ed[j].to));
                }
                if(rd[bel[ed[j].to]]==0)
                    qu.push(bel[ed[j].to]);
            }
    }
    for(int i=1;i<=T;i++)
        if(dis[i]>=1e9-1)
            printf("NO PATH\\n");
        else
            printf("%d\\n",dis[i]);
    return 0;
}

 

以上是关于道路和航线,题解的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2200: [Usaco2011 Jan]道路和航线

道路和航线

AcWing 道路与航线

道路和航线(最短路SPFA优化算法)

[Usaco2011 Jan]道路和航线

道路与航线(toposort+dijkstra)