1.SPFA算法
使用Dijkstra算法,此图的边数比点数的平方要少很多,因此应该使用带堆优化的Dijkstra。
给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。
第一行两个整数n, m。
接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。
1 2 -1
2 3 -1
3 1 2
-2
对于10%的数据,n = 2,m = 2。
对于30%的数据,n <= 5,m <= 10。
对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。
很多时候,给定的图存在负权边,这时类似 Dijkstra 等算法便没有了用武之地,而 Bellman-Ford算法 的复杂度又过高,SPFA算法便派上用场了。简洁起见,我们约定加权 有向图 G不存在负权回路,即最短路径一定存在。如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)。当然,我们可以在执行该算法前做一次 拓扑排序 ,以判断是否存在负权回路,但这不是我们讨论的重点。我们用 数组 d记录每个结点的 最短路径 估计值,而且用 邻接表 来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的 队列 用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行 松弛操作 ,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。定理: 只要最短路径存在,上述SPFA算法必定能求出最小值
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
const int inf = 1<<30;
const int L = 200000;
struct Edges
{
int x,y,w,next;
} e[L<<2];
int head[L];
int dis[L];
int vis[L];
int cnt[L];
void AddEdge(int x,int y,int w,int k)
{
e[k].x = x,e[k].y = y,e[k].w = w,e[k].next = head[x],head[x] = k;
}
int relax(int u,int v,int c)
{
if(dis[v]>dis[u]+c)
{
dis[v] = dis[u]+c;
return 1;
}
return 0;
}
int SPFA(int src)
{
int i;
memset(cnt,0,sizeof(cnt));
dis[src] = 0;
queue<int> Q;
Q.push(src);
vis[src] = 1;
cnt[src]++;
while(!Q.empty())
{
int u,v;
u = Q.front();
Q.pop();
vis[u] = 0;
for(i = head[u]; i!=-1; i=e[i].next)
{
v = e[i].y;
if(relax(u,v,e[i].w)==1 && !vis[v])//v节点的最短距离更新了,但不在队列中,就把该顶点加入队列中。
{
Q.push(v);
vis[v] = 1;
}
}
}
}
int main()
{
int t,n,m;
int i,j;
scanf("%d%d",&n,&m);
memset(e,-1,sizeof(e));
for(i = 1; i<=n; i++)
{
dis[i] = inf;
vis[i] = 0;
head[i] = -1;
}
for(i = 0; i<m; i++)
{
int x,y,w;
scanf("%d%d%d",&x,&y,&w);
AddEdge(x,y,w,i);
}
SPFA(1);
for(i = 2; i<=n; i++)
printf("%d\n",dis[i]);
return 0;
}
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 1000000
#define INF 1<<30
int head[MAXN],m=0;//构造邻接表
struct EDGE
{
int to,next,val;
}edge[MAXN];
int d[MAXN];//到每个顶点的最短距离
int N;//顶点的个数
int V;//边的条数
struct node
{
int id;//顶点
int val;
node(int id,int val):id(id),val(val){}
bool operator <(const node &x) const
{
return val>x.val;
}
};
void add_edge(int u,int v,int val)
{
edge[m].to=v;
edge[m].val=val;
edge[m].next=head[u];
head[u]=m++;
}
void dijkstra(int s)
{
int i;
for(i=1;i<=N;i++)
d[i]=INF;
d[s]=0;
priority_queue<node> q;
q.push(node(s,0));
while(!q.empty())
{
int u,v,val;
u=q.top().id;
val=q.top().val;
q.pop();
int i;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(d[v]>d[u]+edge[i].val)
{
d[v]=d[u]+edge[i].val;
q.push(node(v,d[v]));
}
}
}
}
int main()
{
scanf("%d%d",&N,&V);
fill(head,head+N+1,-1);
int i;
for(i=0;i<V;i++)
{
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
add_edge(a,b,d);
// add_edge(b,a,d);
}
dijkstra(1);
for(i=2;i<=N;i++)
printf("%d\n",d[i]);
//system("pause");
return 0;
}
农夫约翰正在针对一个新区域的牛奶配送合同进行研究。他打算分发牛奶到T个城镇(标号为1..T),这些城镇通过R条标号为(1..R)的道路和P条标号为(1..P)的航路相连。
每一条公路i或者航路i表示成连接城镇Ai(1<=A_i<=T)和Bi(1<=Bi<=T)代价为Ci。每一条公路,Ci的范围为0<=Ci<=10,000;由于奇怪的运营策略,每一条航路的Ci可能为负的,也就是-10,000<=Ci<=10,000。
每一条公路都是双向的,正向和反向的花费是一样的,都是非负的。
每一条航路都根据输入的Ai和Bi进行从Ai->Bi的单向通行。实际上,如果现在有一条航路是从Ai到Bi的话,那么意味着肯定没有通行方案从Bi回到Ai。
农夫约翰想把他那优良的牛奶从配送中心送到各个城镇,当然希望代价越小越好,你可以帮助他嘛?配送中心位于城镇S中(1<=S<=T)。
输入的第一行包含四个用空格隔开的整数T,R,P,S。
接下来R行,描述公路信息,每行包含三个整数,分别表示Ai,Bi和Ci。
接下来P行,描述航路信息,每行包含三个整数,分别表示Ai,Bi和Ci。
1 2 5
3 4 5
5 6 10
3 5 -100
4 6 -100
1 3 -10
NO PATH
5
0
-95
-100
对于20%的数据,T<=100,R<=500,P<=500;
对于30%的数据,R<=1000,R<=10000,P<=3000;
对于100%的数据,1<=T<=25000,1<=R<=50000,1<=P<=50000。
这个题也是采用SPFA的算法,代码如下:(90分 两个测试点超时)
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 200000
#define INF 1<<30
struct EDGE
{
int to,next,dis;
}edge[MAXN];
int m=1;
int d[25000];
int head[25000];
int vis[25000];//用来判断更新过的顶点是不是已经在队列中了
void add_edge(int u,int v,int dis)//用邻接表来保存边
{
edge[m].to=v;
edge[m].dis=dis;
edge[m].next=head[u];
head[u]=m++;
}
void SPFA(int s)//SPFA算法的模板,记住就行了
{
vis[s]=1;
d[s]=0;
queue<int> q;
q.push(s);
int i;
while(!q.empty())
{
int u,v;
u=q.front();
q.pop();
vis[u]=0;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(d[u]!=INF&&d[v]>d[u]+edge[i].dis)
{
d[v]=d[u]+edge[i].dis;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
}
}
}
/*void SPFA(int s)
{
d[s]=0;
int q[25000000];
q[0]=s;
int front=0;
int end=1;
vis[s]=1;
while(front<end)
{
int u,v;
u=q[front];
vis[u]=0;
front++;
int i;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(d[u]!=INF&&d[v]>d[u]+edge[i].dis)
{
d[v]=d[u]+edge[i].dis;
if(!vis[v])
{
vis[v]=1;
q[end++]=v;
}
}
}
}
}*/
int main()
{
int T,R,P,S;
scanf("%d%d%d%d",&T,&R,&P,&S);
int i;
for(i=0;i<=T;i++)
{
head[i]=-1;
vis[i]=0;
d[i]=INF;
}
int x,y,z;
for(i=1;i<=R;i++)
{
scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);
add_edge(y,x,z);
}
for(i=1;i<=P;i++)
{
scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);
}
SPFA(S);
for(i=1;i<=T;i++)
if(d[i]==INF)
printf("NO PATH\n");
else
printf("%d\n",d[i]);
// system("pause");
return 0;
}
如果采用dijkstra 75分 5个测试点超时
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 1000000
#define INF 1<<30
//int head[MAXN],m=0;//构造邻接表
struct EDGE
{
int to,next,val;
}edge[MAXN];
int d[25000];
int head[25000],m=1;
int vis[25000];
//int d[MAXN];//到每个顶点的最短距离
int N;//顶点的个数
int V;//边的条数
struct node
{
int id;//顶点
int val;
node(int id,int val):id(id),val(val){}
bool operator <(const node &x) const
{
return val>x.val;
}
};
void add_edge(int u,int v,int val)
{
edge[m].to=v;
edge[m].val=val;
edge[m].next=head[u];
head[u]=m++;
}
void dijkstra(int s)
{
int i;
for(i=1;i<=N;i++)
d[i]=INF;
d[s]=0;
priority_queue<node> q;
q.push(node(s,0));
while(!q.empty())
{
int u,v,val;
u=q.top().id;
val=q.top().val;
q.pop();
int i;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(d[v]>d[u]+edge[i].val)
{
d[v]=d[u]+edge[i].val;
q.push(node(v,d[v]));
}
}
}
}
int main()
{
/* scanf("%d%d",&N,&V);
fill(head,head+N+1,-1);
int i;
for(i=0;i<V;i++)
{
int a,b,d;
scanf("%d%d%d",&a,&b,&d);
add_edge(a,b,d);
// add_edge(b,a,d);
}
dijkstra(1);
for(i=2;i<=N;i++)
printf("%d\n",d[i]);
system("pause");
return 0;*/
int T,R,P,S;
scanf("%d%d%d%d",&T,&R,&P,&S);
//scanf("%d%d",&T,&R);
int i;
for(i=0;i<=T;i++)
{
head[i]=-1;
vis[i]=0;
d[i]=INF;
}
int x,y,z;
for(i=1;i<=R;i++)
{
scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);
add_edge(y,x,z);
}
for(i=1;i<=P;i++)
{
scanf("%d%d%d",&x,&y,&z);
add_edge(x,y,z);
}
dijkstra(S);
for(i=1;i<=T;i++)
if(d[i]==INF)
printf("NO PATH\n");
else
printf("%d\n",d[i]);
//system("pause");
return 0;
}
2.floyd算法(计算任意两点间的最短距离)
时间复杂度为O(N^3); N为顶点的数量
核心代码
for ( int i = 0; i < 节点个数; ++i )
{
for ( int j = 0; j < 节点个数; ++j )
{
for ( int k = 0; k < 节点个数; ++k )
{
if ( Dis[i][k] + Dis[k][j] < Dis[i][j] )
{
// 找到更短路径
Dis[i][j] = Dis[i][k] + Dis[k][j];
}
}
}
}
应用的例子如下
Cow Contest
-
描述
-
N (1 ≤ N ≤ 100) cows, conveniently numbered 1..N, are participating in a programming contest. As we all know, some cows code better than others. Each cow has a certain constant skill rating that is unique among the competitors.
The contest is conducted in several head-to-head rounds, each between two cows. If cow A has a greater skill level than cow B (1 ≤ A ≤ N; 1 ≤ B ≤ N; A ≠ B), then cow A will always beat cow B.
Farmer John is trying to rank the cows by skill level. Given a list the results of M (1 ≤ M ≤ 4,500) two-cow rounds, determine the number of cows whose ranks can be precisely determined from the results. It is guaranteed that the results of the rounds will not be contradictory.
-
输入
-
* Line 1: Two space-separated integers: N and M
* Lines 2..M+1: Each line contains two space-separated integers that describe the competitors and results (the first integer, A, is the winner) of a single round of competition: A and B
There are multi test cases.The input is terminated by two zeros.The number of test cases is no more than 20.
输出
-
For every case:
* Line 1: A single integer representing the number of cows whose ranks can be determined
样例输入
-
5 5 4 3 4 2 3 2 1 2 2 5 0 0
-
* Line 1: Two space-separated integers: N and M
#include <iostream>
#include <string.h>
#include <cstdio>
using namespace std;
#define MAX 0x7fffffff
const int N=110;
int dis[N][N];
int n,m;
void floyd(){
for(int k=1;k<=n;++k){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(dis[i][k]!=MAX&&dis[k][j]!=MAX&&dis[i][j]>dis[i][k]+dis[k][j]){
dis[i][j]=dis[i][k]+dis[k][j];
}
}
}
}
}
int main(){
//freopen("1.txt","r",stdin);
while(~scanf("%d%d",&n,&m)&&n&&m){
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j)
dis[i][j]=MAX;
}
int x,y;
while(m--){
scanf("%d%d",&x,&y);
dis[x][y]=1;
}
floyd();
int ans=0;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
if(j==i)continue;
if(dis[i][j]==MAX&&dis[j][i]==MAX)
{ans++;break;}
}
}
printf("%d\n",n-ans);
}
return 0;
}