一些基础的跳过了,记录一些实用性强的模板
Dijkstra: O(n2) 适用于 权值为非负 的图的单源最短路径,用斐波那契堆的复杂度O(E+VlgV),
BellmanFord:适用于权值有负值的图的单源最短路径,并且能够检测负圈,复杂度O(VE)
SPFA:适用于权值有负值,且没有负圈的图的单源最短路径,论文中的复杂度O(kE),k为每个节点进入Queue的次数,且k一般<=2,但此处的复杂度证明是有问题的,其实SPFA的最坏情况应该是O(VE).
Floyd:每对节点之间的最短路径。Floyd-Warshall算法的时间复杂度为O(N3),空间复杂度为O(N2)。
先给出结论:
(1)当权值为非负时,用Dijkstra。
(2)当权值有负值,且没有负圈,则用SPFA,SPFA能检测负圈,但是不能输出负圈。
(3)当权值有负值,而且可能存在负圈,则用BellmanFord,能够检测并输出负圈。
(4)SPFA检测负环:当存在一个点入队大于等于V次,则有负环,后面有证明。
但以上比较都是没优化的。
堆优化的dijkstra算法 (洛谷p4479)
1、所有边权均是正数的单源最短路 O(mlogn)
2、适用于稠密图和顶点关系密切 即m和n一个数量级
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=1e5+7;
bool vis[maxn];
typedef pair<int,int>pi;
int n,m,s,dis[maxn],head[maxn],cnt=0;
struct edge{ int to,w,next; }e[maxn*5];
void add(int u,int v,int w){
e[++cnt].w=w;
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
inline void dijkstra(){
for(int i=1; i<=n; i++) dis[i]=inf;
dis[s]=0;
priority_queue<pi>q;
q.push(pi(0,s));
while(!q.empty()){
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x];i;i=e[i].next){
int y=e[i].to;
if(!vis[y]&&dis[y]>dis[x]+e[i].w){
dis[y]=dis[x]+e[i].w;
q.push(pi(-dis[y],y));
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
dijkstra();
for(int i=1;i<=n;i++)printf("%d ",dis[i]);
}
/*
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
0 2 4 3
*/
SPFA(是Bellman-ford的队列优化)
判断有无负环:如果某个点进入队列的次数超过N次则存在负环,但SPFA无法处理带负环的图
一个SPFA入门的讲解
找了个入门题做例子:POJ 3268 Silver Cow Party(有向图多点到star和star到多点问题)
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=1e4+7;
bool vis[maxn];
int n,m,s,dis[maxn],head[maxn],cnt=0;
struct edge{ int to,w,next; }e[maxn*5];
void add(int u,int v,int w){
e[++cnt].w=w;
e[cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void spfa(){
memset(dis,inf,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[s]=0; vis[s]=1;
queue<int>q; q.push(s);
while(!q.empty()){
int x=q.front(); q.pop();
vis[x]=0;
for(int i=head[x];i;i=e[i].next){
int y=e[i].to,w=e[i].w;
if(dis[y]>dis[x]+w){
dis[y]=dis[x]+w;
if(!vis[y]){
q.push(y);
vis[y]=1;
//如果要判断存在负边,加一个In[maxn]数组,存在返回flase
// if(++In[y]>n) return 0;
}
}
}
}
}
int a[maxn],u[maxn],v[maxn],w[maxn],sum;
int main(){
while(~scanf("%d%d%d",&n,&m,&s)){
memset(head,0,sizeof(head)); memset(a,0,sizeof(a)); cnt=0;
for(int i=0;i<m;i++){
scanf("%d%d%d",&u[i],&v[i],&w[i]);
add(u[i],v[i],w[i]);
}
spfa();
for(int i=1;i<=n;i++) a[i]+=dis[i];
memset(head,0,sizeof(head)); cnt=0;
for(int i=0;i<m;i++) add(v[i],u[i],w[i]);
spfa();
for(int i=1;i<=n;i++) a[i]+=dis[i];
sum=0;
for(int i=1;i<=n;i++) sum=max(sum,a[i]);
printf("%d\n",sum);
}
}
Floyd O(n^3)
最不实用,如果数据100以下,可以无脑用。
例题:POJ - 3660 Cow Contest(里面还有闭包关系)
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=100+7;
int a[maxn][maxn],n,m,x,y,sum;
void floyd(){
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++){
if(!a[i][k]) continue;
for(int j=1;j<=n;j++)
a[i][j]|=a[i][k]&a[k][j];
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
memset(a,0,sizeof(a)); sum=0;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
a[x][y]=1;
}
floyd();
for(int i=1;i<=n;i++){
int t=0;
for(int j=1;j<=n;j++){
if(i==j) continue;
if(a[i][j]||a[j][i]) t++;
}
if(t==n-1) sum++;
}
printf("%d\n",sum);
}
}