单源最短路径的种种…
DP的算法
NO.1: only 5 lines – Floyd算法
优缺点:
*优点:
1.可以处理负权值的图
2.算法简单!
缺点:
1.无法处理负权环的情况!
2. 三重循环,无法算大的数据.*
基本概念:
就是将每次设置一个k点作为中转站,在用1~n的i与j二重循环来套进去,利用一个矩阵来储存,如a[i][j]表示i->j的权值. 对每一个点进行松弛,看是否成功!
转移方程:
if(a[i][k]<inf&&a[k][j]<inf&&a[i][k]+a[k][j]<a[i][j])
a[i][j]=a[i][k]+a[k][j];
代码:
#include<stdio.h>
#include <iostream>
using namespace std;
int main() {
int a[100][100],n,m,t1,t2,value;
int inf=999999;
cin>>n;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(i==j) a[i][j]=0;
else a[i][j]=inf;
cin>>m;
for(int i=1; i<=m; i++) {
cin>>t1>>t2>>value;
a[t1][t2]=value;
}
// Floyd-warshall 算法核心
for(int k=1; k<=n; k++)
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(a[i][k]<inf&&a[k][j]<inf&&a[i][k]+a[k][j]<a[i][j])
a[i][j]=a[i][k]+a[k][j];
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++)
if(a[i][j]==inf) cout<<"∞";
else printf("%3d",a[i][j]);
cout<<endl;
}
}
NO.2: Dijksra 算法
优缺点:
优点:
1.没有三重循环!
2.复杂度小
3.基于贪心策略!
缺点:1.无法处理负权值(因为会破坏确定值不变的情况)
基本概念:
就是每次找一个离一号点最近而且没有处理过的数字(book[ ]来储存是否操作)
来将其作为”中转站”来松弛其他的点!用一个dis[ ]数组来储存值!
找最小点的代码:
min=inf;
for(j=1;j<=n;j++)
{
if(book[j]==0 && dis[j]<min)
{
min=dis[j];
u=j;
}
}
代码 –O(n^2):
#include <stdio.h>
int main()
{
int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值
//读入n和m,n表示顶点个数,m表示边的条数
scanf("%d %d",&n,&m);
//初始化
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(i==j) e[i][j]=0;
else e[i][j]=inf;
//读入边
for(i=1;i<=m;i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程
for(i=1;i<=n;i++)
dis[i]=e[1][i];
//book数组初始化
for(i=1;i<=n;i++)
book[i]=0;
book[1]=1;
//Dijkstra算法核心语句
for(i=1;i<=n-1;i++) //每次循环定位一个数
{
//找到离1号顶点最近的顶点
min=inf;
for(j=1;j<=n;j++)
{
if(book[j]==0 && dis[j]<min)
{
min=dis[j];
u=j;
}
}
book[u]=1;
for(v=1;v<=n;v++)
{
if(e[u][v]<inf)
{
if(dis[v]>dis[u]+e[u][v])
dis[v]=dis[u]+e[u][v];
}
}
}
//输出最终的结果
for(i=1;i<=n;i++)
printf("%d ",dis[i]);
getchar();
getchar();
return 0;
}
代码 –复杂度O(n logn)
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> P;
const int N=1000005;
const int inf=999999;
struct edge
{
int to,cost;
bool road;
edge(bool road,ll to=0,ll cost=0):road(road),to(to),cost(cost) {}
};
int n,m,k;
vector<edge> G[N];
ll d[N];
void dijstra(long long s)
{
//С¸ù¶ÑµÄ×ö·¨!
priority_queue<P,vector<P>,greater<P> > que;
fill(d,d+n,inf);
d[1]=0;
que.push(P(0,1));
while(!que.empty())
{
P p=que.top();
que.pop();
int v=p.second;
if(d[v]<p.first) continue;
for(int i=0; i<G[v].size(); i++)
{
edge e=G[v][i];
if(d[e.to]>d[v]+e.cost)
d[e.to]=d[v]+e.cost;
que.push(P(d[e.to],e.to));
}
}
}
int main()
{
//......
return 0;
}
NO.3: Bellman-Ford 算法
声明:
如有雷同,纯属巧合,请勿介意!
优缺点:
优点:
1.可以判断负权回路
2.好像可以处理环哦!
3.简易易学(啊哈!)
缺点:
1.我觉得挺好的!
基本思路:
将原来1->i的点局设置为∞,在之后的m-1轮之中可以将其松弛,
就是看
k==1->1点通过1条边能够到达的最小
k==2->1点通过2条边能够达到的最小
……
k==n->1点…..n…最小
即:
for(int k=1; k<=n-1; k++) //n-1条边的分别确定 即k->k条边
for(int i=1; i<=m; i++)
if(dis[v[i]]>dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
所以说就是每次循环找出所有输入边的情况,在进行操作k-1轮!
算法的过程:
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
int n,m,dis[1000],u[1000],v[1000],w[1000];
int inf=999999;
cin>>n>>m;
for(int i=1; i<=m; i++)
cin>>u[i]>>v[i]>>w[i];
for(int i=1; i<=n; i++)
dis[i]=inf;
dis[1]=0;
//Bellman-Ford 算法的核心;
for(int k=1; k<=n-1; k++) //n-1条边的分别确定 即k->k条边
for(int i=1; i<=m; i++)
if(dis[v[i]]>dis[u[i]]+w[i])
dis[v[i]]=dis[u[i]]+w[i];
for(int i=1; i<=n; i++)
cout<<dis[i]<<" ";
}
/*
测试数据附赠:
Input:
5 5
2 3 2
1 2 -3
1 5 5
4 5 2
3 4 3
Output:
0 -3 -1 2 4
*/
OR 队列优化的Bellaman
#include <stdio.h>
#include <iostream>
using namespace std;
int main() {
int n,m;
int u[10],v[10],w[10];
int first[10],next[10];
int dis[6]= {0},book[6]= {0};
int que[101]= {0},head=1,tail=1;
int inf=9999999;
cin>>n>>m;
for(int i=1; i<=n; i++)
dis[i]=inf;
dis[1]=0;
//³õʼ»¯bookÊý×é,Ò»¿ªÊ¼Îª0,±íʾ¾ù²»ÔÚ¶ÓÁÐÖÐ
for(int i=1; i<=n; i++)
book[i]=0;
//½«ÁÚ½Ó±í³õʼ»¯Îª-1 ,±íʾÔÝʱ¶¼Ã»Óб߼ÓÈë
for(int i=1; i<=n; i++)
first[i]=-1;
for(int i=1; i<=m; i++) {
//¶ÁÈëÿһÌõ±ß
cin>>u[i]>>v[i]>>w[i];
next[i]=first[u[i]];
first[u[i]]=i;
}
//1ºÅµãÈë¶Ó
que[tail]=1;
tail++;
book[1]=1;//±íʾÈë¶Ó
int k;
while(head<tail) {
k=first[que[head]];//¶ÓÊ×µÄÊý¾Ý
while(k!=-1) {
if(dis[v[k]]>dis[u[k]]+w[k])
dis[v[k]]=dis[u[k]]+w[k];
//ÓÃÒ»¸öbookÊý×éÅжÏÊÇ·ñÔÚ¶ÓÁÐÀï
if(book[v[k]]==0)
//Èç¹û²»Ê¹ÓÃbookÀ´¼Ç¼¾ÍҪÿ´Î´Óhead~tailɨһ±é!
{
que[tail]=v[k];
tail++;
book[v[k]]=1;//±íʾv[k]ÒѾÈëÁÐ
}
k=next[k];
}
//³ö¶Ó
book[que[head]]=0;
head++;
}
for(int i=1; i<=n; i++)
printf("%d ",dis[i]);
return 0;
}
注:如有兴趣,请加关注哦!