最短路 SPFA 判断负环 静态邻接表(链式前向星) HDU 2544 最短路 POJ 3259 Wormholes

两道最短路的题目,第一个题目中说“输入保证至少存在1条商店到赛场的路线”,即图中不存在负环,直接套用SPFA求解即可。第二题,题目大意就是让判断图中是否存在负环。其实思路也很简单,就是设置一个cnt[ ]数组存各节点进队次数,如果cnt[i]大于等于节点个数,则存在负环。


另外,第二个题,为了解决节点数量过大的情况,另附静态邻接表代码。


还有一件事,最后一份代码中还使用了双端队列优化SPFA算法。假设要加入的结点是j,队首结点是i,如果dis[j] < dis[i],则将j插入到队首;否则,插入到队尾。因为将更短的dis[j]放到队首优先操作可以得到更优的最短路路线,(如果先处理更大的dis[j1]的话,后来在处理更小的dis[j2]时,之前的处理节点j1还是有一定的可能重新回到队列中,)从而减少不必要的重复操作。



HDU 2544 最短路

Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Problem Description
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

 

Input
输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。
输入保证至少存在1条商店到赛场的路线。
 

Output
对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间
 

Sample Input
  
  
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
 

Sample Output
  
  
3 2


代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

int N, M;//nodenum,edgenum
int dis[105];//d[i]记录源点到i的最短距离
bool vis[105];//标记节点是否在队列中
const int INF = 1<<30;
int c[105];//统计每个节点入队的次数
bool loop;//判断是否有负回路
int p[105][105];

void SPFA(int start)
{
    //初始化
    queue <int> q;
    memset(vis, 0, sizeof(vis));
    for (int i=1; i<=N; i++)//注意节点标号从1开始
        dis[i] = INF;
    dis[start] = 0;
    vis[start] = 1;
    q.push(start);

    while (!q.empty())
    {
        int temp = q.front();
        q.pop();
        vis[temp] = 0;
        for (int i=1; i<=N; i++)
        {
            if (dis[temp]+p[temp][i] < dis[i])
            {
                dis[i] = dis[temp]+p[temp][i];
                if (!vis[i])
                {
                    q.push(i);
                    vis[i] = 1;
                }
            }
        }
    }
}

int main()
{
    int a, b;
    while (scanf ("%d %d",&N,&M) != EOF)
    {
        if (!N && !M)
            break;
        for (int i=1; i<=N; i++)
        {
            for (int j=1; j<=N; j++)
            {
                if (i == j)
                    p[i][j] = 0;
                else
                    p[i][j] = INF;
            }
        }
        for (int i=1; i<=M; i++)
        {
            scanf ("%d %d",&a,&b);
            scanf ("%d",&p[a][b]);
            p[b][a] = p[a][b];
        }
        SPFA(1);
        printf ("%d\n",dis[N]);
    }
    return 0;
}


POJ 3259 Wormholes

Time Limit: 2000MS Memory Limit: 65536K

Description

While exploring his many farms, Farmer John has discovered a number of amazing wormholes. A wormhole is very peculiar because it is a one-way path that delivers you to its destination at a time that is BEFORE you entered the wormhole! Each of FJ's farms comprises N (1 ≤ N ≤ 500) fields conveniently numbered 1..N,M (1 ≤ M ≤ 2500) paths, and W (1 ≤ W ≤ 200) wormholes.

As FJ is an avid time-traveling fan, he wants to do the following: start at some field, travel through some paths and wormholes, and return to the starting field a time before his initial departure. Perhaps he will be able to meet himself :) .

To help FJ find out whether this is possible or not, he will supply you with complete maps toF (1 ≤ F ≤ 5) of his farms. No paths will take longer than 10,000 seconds to travel and no wormhole can bring FJ back in time by more than 10,000 seconds.

Input

Line 1: A single integer, F. F farm descriptions follow.
Line 1 of each farm: Three space-separated integers respectively: N, M, and W
Lines 2.. M+1 of each farm: Three space-separated numbers ( S, E, T) that describe, respectively: a bidirectional path between S and E that requires T seconds to traverse. Two fields might be connected by more than one path.
Lines M+2.. M+ W+1 of each farm: Three space-separated numbers ( S, E, T) that describe, respectively: A one way path from S to E that also moves the traveler back T seconds.

Output

Lines 1.. F: For each farm, output "YES" if FJ can achieve his goal, otherwise output "NO" (do not include the quotes).

Sample Input

2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8

Sample Output

NO
YES

Hint

For farm 1, FJ cannot travel back in time.
For farm 2, FJ could travel back in time by the cycle 1->2->3->1, arriving back at his starting location 1 second before he leaves. He could start from anywhere on the cycle to accomplish this.


邻接矩阵 代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

int N, M, H;//nodenum,edgenum,holenum
int dis[505];//d[i]记录源点到i的最短距离
bool vis[505];//标记节点是否在队列中
const int INF = 1<<30;
int cnt[505];//统计每个节点入队的次数
//bool loop;//判断是否有负回路
int p[505][505];

bool SPFA(int start)
{
    //初始化
    queue <int> q;
    memset(vis, 0, sizeof(vis));
    memset(cnt, 0, sizeof(cnt));
    for (int i=1; i<=N; i++)//注意节点标号从1开始
        dis[i] = INF;
    dis[start] = 0;
    vis[start] = 1;
    cnt[start] ++;
    q.push(start);

    while (!q.empty())
    {
        int temp = q.front();
        q.pop();
        vis[temp] = 0;
        if (cnt[temp] >= N)//判断负环
            return false;
        for (int i=1; i<=N; i++)
        {
            if (dis[temp]+p[temp][i] < dis[i])
            {
                dis[i] = dis[temp]+p[temp][i];
                if (!vis[i])
                {
                    q.push(i);
                    vis[i] = 1;
                    cnt[i] ++;
                }
            }
        }
    }
    return true;
}

int main()
{
    int T, a, b, cost;
    cin >> T;
    for (int k=1; k<=T; k++)
    {
        scanf ("%d %d %d",&N,&M,&H);
        for (int i=1; i<=N; i++)
        {
            for (int j=1; j<=N; j++)
            {
                if (i == j)
                    p[i][j] = 0;
                else
                    p[i][j] = INF;
            }
        }
        for (int i=1; i<=M; i++)
        {
            scanf ("%d %d %d",&a,&b,&cost);
            if (cost < p[a][b])
                p[b][a] = p[a][b] = cost;
        }
        for (int i=1; i<=H; i++)
        {
            scanf ("%d %d %d",&a,&b,&cost);
            if ((-cost) < p[a][b])
                p[a][b] = (-cost);
        }
        if (SPFA(1))
            printf ("NO\n");
        else
            printf ("YES\n");
        //printf ("%d\n",dis[N]);
    }
    return 0;
}

静态邻接表 代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
#include<cmath>
#define LL long long
#define MAXN 1000010
using namespace std;
const int INF=0x3f3f3f3f;
int n,m,tot,k;
int head[MAXN];
struct node
{
    int from;
    int to;
    int w;
    int next;
} edge[MAXN];
int dis[MAXN];
bool inq[MAXN];//vis[]
int cnt[MAXN];
int path[MAXN];
void init()
{
    memset(head,-1,sizeof(head));
    tot=0;
}
void add(int from,int to,int w)
{
    edge[tot].from=from;
    edge[tot].to=to;
    edge[tot].w=w;
    edge[tot].next=head[from];
    head[from]=tot++;
}
bool spfa(int st)
{
    deque<int>q;
    memset(dis,INF,sizeof(dis));
    memset(inq,false,sizeof(inq));
    memset(cnt,0,sizeof(cnt));
    dis[st]=0;
    q.push_back(st);
    inq[st]=true;
    cnt[st]++;
    while(!q.empty())
    {
        int now=q.front();
        q.pop_front();
        inq[now]=false;
        if(cnt[now]>=n)
        {
            return false;
        }

        for(int i=head[now]; i!=-1; i=edge[i].next)
        {
            int nex=edge[i].to;
            if(dis[nex]>dis[now]+edge[i].w)
            {
                dis[nex]=dis[now]+edge[i].w;
                if(!inq[nex])
                {
                    inq[nex]=true;
                    cnt[nex]++;
                    if(!q.empty()&&dis[nex]<dis[q.front()])//STL优化,假设要加入的结点是j,队首结点是i,如果dis[j] < dis[i],则将j插入到队首;否则,插入到队尾。
                    {
                        q.push_front(nex);
                    }
                    else
                    {
                        q.push_back(nex);
                    }
                }
            }
        }
    }
    return true;
}
int main()
{
    //freopen("D:\in.txt","r",stdin);
    int T,cas,i,j,u,v,w;
    scanf("%d",&T);
    for(cas=1; cas<=T; cas++)
    {
        scanf("%d%d%d",&n,&m,&k);
        init();
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
            add(v,u,w);
        }
        while(k--)
        {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,-w);
        }
        if(spfa(1))
        {
            printf("NO\n");
        }
        else
        {
            printf("YES\n");
        }
    }
    return 0;
}


      


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值