两道最短路的题目,第一个题目中说“输入保证至少存在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)输入保证至少存在1条商店到赛场的路线。
2 1 1 2 3 3 3 1 2 5 2 3 5 3 1 2 0 0
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 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
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 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;
}