POJ--Til the Cows Come Home(Bellman-Ford的队列优化)

Bessie is out in the field and wants to get back to the barn to get as much sleep as possible before Farmer John wakes her for the morning milking. Bessie needs her beauty sleep, so she wants to get back as quickly as possible. 

Farmer John's field has N (2 <= N <= 1000) landmarks in it, uniquely numbered 1..N. Landmark 1 is the barn; the apple tree grove in which Bessie stands all day is landmark N. Cows travel in the field using T (1 <= T <= 2000) bidirectional cow-trails of various lengths between the landmarks. Bessie is not confident of her navigation ability, so she always stays on a trail from its start to its end once she starts it. 

Given the trails between the landmarks, determine the minimum distance Bessie must walk to get back to the barn. It is guaranteed that some such route exists.
Input
* Line 1: Two integers: T and N 

* Lines 2..T+1: Each line describes a trail as three space-separated integers. The first two integers are the landmarks between which the trail travels. The third integer is the length of the trail, range 1..100.
Output
* Line 1: A single integer, the minimum distance that Bessie must travel to get from landmark N to landmark 1.
Sample Input
5 5
1 2 20
2 3 30
3 4 20
4 5 20
1 5 100
Sample Output
90

前面已经写过Bellman-Ford的算法了,但是这不是最简洁的,算法,一直在追求更简洁

书上说这种算法根据Be算法优化的

但是我感觉更像是用  Di算法优化的(个人意见)

为什么那?

Di算法是根据点来找最短路,这种算法也是,两种方法更新最短路的方法几乎一样

为什么说是Di的优化,哪里优化了

首先回顾一下Di算法为何不能有负权边

DI算法的时间比较短,是什么保证了它比Be的时间更短

是Di算法中的每一个点只找一次,即当所有边权为正时

不会存在一个路程更短的点来更新相邻的点,所以这个点真真正正只用找一次

下一次就不必动了,但是如果有负权边当拓展到负权边的时候又会产生新的最短路

前面的就需要再动一动了,这时候Di算法就出错了,算法的核心不成立了

但是!!!!

Be优化算法(目前介绍的算法)用了一个队列把需要更改的点存起来再更改一遍

通俗来说就是 我每次更新一个最短路,那么就有一个点有了新的最短路径,那么我把这个点

存起来,等等再来更新这个点,直到我存的点全部更新完

这样即使有负权边前面的点的最短路变了,但是我把点存起来了,一会再更新

这样负权边也没有影响了

但是!!!!

相对的,由于我又扭头更新了几次数据,那么我的时间复杂度也肯定要增加

所以也就没有Di算法快

ps :以上均为一个菜鸟的个人看法,如有错误,请大犇指出!


下面就上一下代码

#include<stdio.h>
#include<queue>
using namespace std;
int u[4005],v[4005],w[4005];
int first[4001],next[4001];
int dis[1005],book[1005];
#define inf 99999999
int main()
{
    int t,n;
    queue<int> q;
    while(scanf("%d%d",&t,&n)!=EOF)
    {
        int k,a,b,c,d;
        for(int i=1; i<=2*t; i++) //first 初始化一下准备开始建图
        {
            first[i]=-1;
        }
        for(int i=1;i<=n;i++)
        {
            book[i]=0;     //初始化 表示栈里面没有元素
            dis[i]=inf;
        }
        dis[1]=0;
        for(int i=1; i<=2*t; i+=2)
        {
            scanf("%d%d%d",&a,&b,&c);
            u[i]=a,v[i]=b,w[i]=c;
            u[i+1]=b,v[i+1]=a,w[i+1]=c;
        }
        /*for(int i=1;i<=2*t;i++)
        {
            printf("%d %d %d*\n",u[i],v[i],w[i]);
        }*/
        for(int i=1;i<=2*t;i++)
        {
            next[i]=first[u[i]];
            first[u[i]]=i;
        }
        /*for(int i=1;i<=2*t;i++)
        {
            printf("[%2d] %2d %2d*.*\n",i,first[i],next[i]);
        }*/
        q.push(1);
        book[1]=1;
        while(!q.empty())
        {
            d=q.front();
            k=first[d];  // 注意!进去的是点
            while(k!=-1)     //扫描当前点的所有边(注意是双向的)
            {
                if(dis[v[k]]>dis[u[k]]+w[k])
                {
                    dis[v[k]]=dis[u[k]]+w[k];
                    if(book[v[k]]!=1)
                    {
                        q.push(v[k]);
                        book[v[k]]=1;
                    }
                }
                k=next[k];
                //printf("%d*\n",k);
                /*for(int i=1;i<=n;i++)
                {
                    printf("%d ",dis[i]);
                }
                printf("\n");
                for(int i=1;i<=n;i++)
                {
                    printf("%d ",book[i]);
                }
                printf("\n");*/
            }
            book[d]=0;
            q.pop();
        }
        printf("%d\n",dis[n]);
    }
}

提交超时!!!

我再次DBUG了很长时间!!好气啊

错误就一处

那就是first的数组开大了

而且在初始化first初始化的也多了!!

我觉得这才多初始化2000次,应该不会超时吧!事实上超时

以后写代码的话能省就省,能优化一点就立即优化!

下面再上一下AC代码 就修改了两处

#include<stdio.h>  
#include<queue>  
using namespace std;  
int u[4005],v[4005],w[4005];  
int first[1005],next[4005];  
int dis[1005],book[1005];  
#define inf 99999999  
int main()  
{  
    int t,n;  
    queue<int> q;  
    while(scanf("%d%d",&t,&n)!=EOF)  
    {  
        int k,a,b,c,d;  
        for(int i=1; i<=n; i++) //first 初始化一下准备开始建图  
        {  
            first[i]=-1;  
        }  
        for(int i=1;i<=n;i++)  
        {  
            book[i]=0;     //初始化 表示栈里面没有元素  
            dis[i]=inf;  
        }  
        dis[1]=0;  
        for(int i=1; i<=2*t; i+=2)  
        {  
            scanf("%d%d%d",&a,&b,&c);  
            u[i]=a,v[i]=b,w[i]=c;  
            u[i+1]=b,v[i+1]=a,w[i+1]=c;  
        }  
        /*for(int i=1;i<=2*t;i++) 
        { 
            printf("%d %d %d*\n",u[i],v[i],w[i]); 
        }*/  
        for(int i=1;i<=2*t;i++)  
        {  
            next[i]=first[u[i]];  
            first[u[i]]=i;  
        }  
        /*for(int i=1;i<=2*t;i++) 
        { 
            printf("[%2d] %2d %2d*.*\n",i,first[i],next[i]); 
        }*/  
        q.push(1);  
        book[1]=1;  
        while(!q.empty())  
        {  
            d=q.front();  
            k=first[d];  // 注意!进去的是点  
            while(k!=-1)     //扫描当前点的所有边(注意是双向的)  
            {  
                if(dis[v[k]]>dis[u[k]]+w[k])  
                {  
                    dis[v[k]]=dis[u[k]]+w[k];  
                    if(book[v[k]]!=1)  
                    {  
                        q.push(v[k]);  
                        book[v[k]]=1;  
                    }  
                }  
                k=next[k];  
                //printf("%d*\n",k);  
                /*for(int i=1;i<=n;i++) 
                { 
                    printf("%d ",dis[i]); 
                } 
                printf("\n"); 
                for(int i=1;i<=n;i++) 
                { 
                    printf("%d ",book[i]); 
                } 
                printf("\n");*/  
            }  
            book[d]=0;  
            q.pop();  
        }  
        printf("%d\n",dis[n]);  
    }  
} 








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值