spfa算法 Easy sssp

输入数据给出一个有N(2 <= N <= 1,000)个节点,M(M <= 100,000)条边的带权有向图.
要求你写一个程序, 判断这个有向图中是否存在负权回路. 如果从一个点沿着某条路径出发, 又回到了自己, 而且所经过的边上的权和小于0, 就说这条路是一个负权回路.
如果存在负权回路, 只输出一行-1;
如果不存在负权回路, 再求出一个点S(1 <= S <= N)到每个点的最短路的长度. 约定: S到S的距离为0, 如果S与这个点不连通, 则输出NoPath.

INPUT:
第一行: 点数N(2 <= N <= 1,000), 边数M(M <= 100,000), 源点S(1 <= S <= N);
以下M行, 每行三个整数a, b, c表示点a, b(1 <= a, b <= N)之间连有一条边, 权值为c(-1,000,000 <= c <= 1,000,000)

OUTPUT:
如果存在负权环, 只输出一行-1, 否则按以下格式输出
共N行, 第i行描述S点到点i的最短路:
如果S与i不连通, 输出NoPath;
如果i = S, 输出0;
其他情况输出S到i的最短路的长度

INPUT:
6 8 1
1 3 4
1 2 6
3 4 -7
6 4 2
2 4 5
3 6 3
4 5 1
3 5 4

OUTPUT:
0
6
4
-3
-2
7

注意:
题目说的不是很清楚,给出的图不一定是完全联通图,有些是断开的几个图,所以在判断的源点是否有环以外还要分别对不同的点进行spfa呀。再进行分别的判断和输出。

有几个优化:
1.可以先判断是否有负权自环,有则直接输出-1
2.在枚举的过程中,当这个顶点的最短路(d[i])<0时,有负权回路,输出-1.

【参考程序】:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
long queue[1001],a[1001],psum[1001],dis[1001],l[1001][1001],cost[1001][1001];
long
n,m,s,i,j;
bool hash[1001],bk
;
void spfa(int
s)
{

    int
head,tail,start,now,i;
   
for (i=1;i<=n;i++
)
    {

        dis[i]=0xfffffff
;
        psum
[i]=0
;
        hash
[i]=false
;
    }

    head=tail=1;hash[s]=true
;
    psum[s]
=1;dis[s]=0;queue[1]=
s;
   
while (head<=tail
)
    {

        start=queue[(head-1)%n+1];
        hash[start]=true
;
       
for (i=1;i<=l[start][0];i++
)
        {

            now=l[start][i];
            if (dis[now]>dis[start]+cost[start][now])
            {
                dis[now]=dis[start]+cost[start][now];
                if (!hash[now])
                {
                    hash[now]=true
;
                    tail
++
;
                    queue
[(tail-1)%n+1]=now
;
                    psum
[now]++
;
                   
if (psum[now]>
n)
                    {
//记录每个点进队的次数(判断环的关键}
                        bk=false
;
                       
return
;
                    }

                }
            }
        }
        head++
;
        hash
[start]=false
;
       
if (dis[s]<0
)
        {
//判断环的一个优化
            bk=false
;
           
return
;
        }

    }
}
void
output()
{

    bk=true
;
    spfa(s);
   
if (!bk
)
    {

        printf("-1/n"
);
       
return
;
    }

    memcpy(a,dis,sizeof(long)*(n+1
));
   
for (i=1;i<=n;i++
)
     
if (a[i]==0xfffffff
)
      {

            bk=true
;
            spfa(i);
           
if (!bk
)
            {

                printf("-1/n"
);
               
return
;
            }

      }
    for (i=1;i<=n;i++
)
     
if (a[i]==0xfffffff) printf("NoPath/n"
);
     
else printf("%d/n",a[i]);
}
void
input()
{

    scanf("%d%d%d",&n,&m,&
s);
   
for (i=1;i<=n;i++
)
     
for (j=1;j<=n;j++
)
       
if (i==j) cost[i][j]=0
;
       
else cost[i][j]=0xfffffff
;
    memset(l
,0,sizeof
(l));
   
int
x,y,c;
   
for (i=1;i<=m;i++
)
    {

        scanf("%d%d%d",&x,&y,&c
);
       
if (c<cost[x][y])
        {
            cost[x][y]=c
;
            l
[x][0]++
;
            l
[x][l[x][0]]=y
;
        }

    }
}
int
main()
{

    input();
    output();
    system(
"pause"
);
   
return 0
;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值