单源最短路径

单源最短路径的种种…

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;

}

注:如有兴趣,请加关注哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值