牛慢跑

牛慢跑

据说是\(k\)短路模板,要用\(A^*\),然而我不会。我是用拓扑排序加堆优化广搜水过去的。第一道完全靠自己做出来的紫题,调了两个小时,交了两遍。果然我还是太菜了。

正解的话,可以看红太阳的博客

题面

给出\(n\)个点\(m\)条边的有向无环图,求从\(n\)\(1\)的前\(k\)条最短路的值。

\(1≤N≤1000 ,1 <= M <= 10000 ,1≤K≤100\)

输入格式

\(1\)行:三个以空格分隔的整数:\(N,M\)\(K\).

\(2..M + 1\)行:第\(i + 1\)行描述了使用三个以空格分隔的整数的下坡牛路径:\(X_i,Y_i\)和$D_i(1≤Di≤1000000) $

输出格式

\(1..K\):行\(i\)包含第\(i\)个最短路径的长度,如果不存在这样的路径,则为\(-1\)。如果多次出现最短路径长度,请务必在输出中多次列出。

输入样例

5 8 7 
5 4 1 
5 3 1 
5 2 1 
5 1 1 
4 3 4 
3 1 1 
3 2 1 
2 1 1 

输出样例

1 
2 
2 
3 
6 
7 
-1 

拓扑排序加堆优化广搜

以下是我的神奇做法。

要找前\(k\)条最短路,怎么找?我观察了一下样例的图,发现每个点的路径长度会被更新很多次,但都是由直接指向他的点更新来的(废话),也就是说,他前面的点怎样更新的我不关心,我只关心这个点前\(k\)条最短路的长度。

那直接用堆存一下就好了。

但是我们要保证他前面的点已经更新完了。前面说了这是一个有向无环图,自然想到拓扑排序。

但我之前犯了一个错误,它花费了我一个多小时的时间。这个题要求必须从\(n\)号点出发,但是有些点\(n\)号点到不了,它们一开始入度就为零。如果不把这些点的影响去掉,后面就会出错。果然我菜鸡的本质难以掩盖。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
long long read(){
    long long x=0;int f=0;char c=getchar();
    while(c<'0'||c>'9')f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return f?-x:x;
}

int n,m,k;
struct Dier{//前向星
    int to,next,w;
}a[10004];
int head[1001],cnt;
void add(int x,int y,int z){//单向边
    a[++cnt].to=y,a[cnt].next=head[x],a[cnt].w=z,head[x]=cnt;
}

priority_queue<int,vector<int>,greater<int> >q[1005];//小根堆
int in[1001],Q[1001],h,t;
//  入度     队列

int main(){
    n=read(),m=read(),k=read();
    for(int i=1,x,y,z;i<=m;++i){
        x=read(),y=read(),z=read();
        add(x,y,z),in[y]++;
    }
    
    //拓扑
    for(int i=n;i;--i)
        if(!in[i]) Q[++t]=i;
    for(int i=1;i<=t;++i){
        if(Q[i]==n) continue;//n号点的影响不应该去掉
        int u=Q[i];
        for(int i=head[u],v;v=a[i].to,i;i=a[i].next){
            in[v]--;
            if(!in[v]) Q[++t]=v;//斩草不除根,后面麻烦无穷
            //只要不是从n号点直接或间接转移过来的入度都要去掉,因为他们会妨碍我们后面的入队
        }
    }
    
    t=1,Q[1]=n;//初始化一下
    q[n].push(0);
    while(h<t){//广搜
        int u=Q[++h],ls=0;
        while(u!=1&&!q[u].empty()&&++ls<=k){//只取前k条就好
          //保证1号点的距离不会被pop掉
            for(int i=head[u],v;v=a[i].to,i;i=a[i].next)
                q[v].push(q[u].top()+a[i].w);//更新距离
            q[u].pop();
        }
        //计算入度一定要独立出来,不能和更新距离放在一起,因为那样会导致入度被减很多次
        for(int i=head[u],v;v=a[i].to,i;i=a[i].next){
            in[v]--;
            if(!in[v]) Q[++t]=v;
        }
    }
    while(!q[1].empty()&&k){//输出答案
        printf("%d\n",q[1].top()),q[1].pop(),k--;
    }
    for(int i=1;i<=k;++i) printf("-1\n");//有可能并没有k条路可以走
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kylinbalck/p/9888219.html
今日推荐