最短路算法

与最小生成树有些不一样.在这里提出三种算法.
dijkstra算法,是最普通也是最简单的.与prim算法有些类似,但适用范围又不一样,有打印路径和不打印路径的小分法.注意不能用于权值有负的图!!!(而且可以用优先队列优化,可以查一下模板,也比较简单).
首先是不打印路径的dijkstra,类似于DP.(打印单源路径,即某一特定点到其他点的距离)

最近发现一个坑点,初始化地图时最好放在输入前面,然后输入信息时要求小于对应地图的位置时才存入地图中,因为我们要求的是最短路,必须保证地图中是两点的最短距离!!!若之后又输入了某点的信息后距离是比之前的更长的则不能替换的,否则求出来的就不是最短路了.!!!

#include
#include
#include
using namespace std;
const int maxp = 2005;
const int INF= 1000000;
int edge[maxp][maxp];
int near[maxp];
int low[maxp];
int n;

void dijkstra(int u)//从u点开始找出距离所有的点最短距离.
{   memset(low,0,sizeof(low));
    for(int i=0;i

打印路径的.

#include
#include
#define INF 1000000 //无穷大
#define maxn 20   //顶点个数的最大值
int n;//顶点个数
int Edge[maxn][maxn];
int s[maxn];
int dist[maxn];
int path[maxn];//因为要打印路径,所以需要一个数组保存这条边来自于那一条边,这样回溯输出便可输出路径.

void dijkstra(int v0)//求v0到其他点的最短路径.
 {  int i,j,k;
    for(i=0;i0;j--)
            printf("%d-->",shortest[j]);
        printf("%d\n",shortest[0]);
    }
}

第二种 最暴力 的算法,floyd(适用于点数比较少的情况,允许有权值为负的边)(可以打印任意两点间的最短距离)(复杂度飞常高,不是一定要用,则尽量不要用)

#include
#include
#include
#include
using namespace std;
const int maxn=1e4+5;
const int inf=1e9+7;
int edge[maxn][maxn];//点i到j的距离.
int n;
void floyd()//可以找到任意两点的最短距离.
{
    for(int k=0;k

第三种,SPFA算法,这个算法是最好的,也可以适用于权值有负的边,时间复杂度也是最低的.(也是可以优化的+SLF,可以去网上搜搜模板)(求源点到其余点的最短距离)

(这个是单纯求最短路的,下面还有用这个算法去求该图有无负环的)
(判断负环就是对每一个入队点数进行标记,当入队次数超过了一定的范围是,可以判断该图中存在负环可以上网搜搜(一般图中为2次,具体怎么证明的也不是很清楚))
#include
#include
#include
#include
#include
#define CLR(x) memset(x,0,sizeof(x))
using namespace std;
const int maxn=1e6;
const int maxp=1e4+5;
const int eps=1e-6;
const int inf=1e9;      //这个inf必须足够大,否则会影响后面的判断的.(int 不够 就用 ll  )
int cas=1;
int edge[maxp][maxp];
int vis[maxp],dis[maxp];
queueq;
int n,m;
void spfa(int u)
{
    CLR(vis);
    vis[u]=1;
    q.push(u);
    dis[u]=0;
    while(!q.empty())
    {
        int tmp=q.front();
        q.pop();
        vis[tmp]=0;//消除标记.
        for(int i=1;i<=n;i++){
            if(dis[i] > edge[tmp][i]+dis[tmp]){
                dis[i]=edge[tmp][i]+dis[tmp];
                if(!vis[i]){
                    vis[i]=1;//进行入队标记.
                    q.push(i);
                }
            }
        }
    }
}
int main()//水题是hdu2544(三种方法都适用)
{
    while(~scanf("%d %d",&n,&m)){
        if(n==0 && m==0)
            break;
       for(int i=1;i<=n;i++){
            dis[i]=inf;
            for(int j=1;j<=n;j++){
                if(i==j)   edge[i][j]=0;
                else  edge[i][j]=inf;
            }
        }
        for(int i=0;i

所以题目要是要求求任意两点间的最短距离,则用floyd算法

spfa算法的栈写法(其实跟队列是基本一样的,就是把队列的地方改成栈就可以了,就是可能内存或运行效率有点不同,看下知道有这种写法就可以了)

#include
#include
#include
#include
#include
#define CLR(x) memset(x,0,sizeof(x))
using namespace std;
const int maxn=1e6;
const int maxp=1e4+5;
const int eps=1e-6;
const int inf=1e9;    //这个inf必须足够大,否则会影响后面的判断的.(int 不够 就用 ll  )
int cas=1;
int edge[maxp][maxp];
int vis[maxp],dis[maxp];
stacks;
int n,m;
void spfa_dfs(int u)
{
    CLR(vis);
    vis[u]=1;
    s.push(u);
    dis[u]=0;
    while(!s.empty())
    {
        int tmp=s.top();
        s.pop();
        vis[tmp]=0;
        for(int i=1;i<=n;i++){
            if(dis[i] > edge[tmp][i]+dis[tmp]){
                dis[i]=edge[tmp][i]+dis[tmp];
                if(!vis[i]){
                    vis[i]=1;
                    s.push(i);
                }
            }
        }
    }
}
int main()//水题是hdu2544(三种方法都适用)
{
    while(~scanf("%d %d",&n,&m)){
        if(n==0 && m==0)
            break;
       for(int i=1;i<=n;i++){
            dis[i]=inf;
            for(int j=1;j<=n;j++){
                if(i==j)   edge[i][j]=0;
                else  edge[i][j]=inf;
            }
        }
        for(int i=0;i

附上有临接链表写的spfa,因位大部分都是用的这种方法写的,据说是更快,更省内存.(如果要写spfa算法的话,就用这种方法写,就不要用上面两种方法了,避免超时!)
这是水题 poj --- 3013 题目在此的spfa写法.
(对于最短路中,dij要T的就一般用spfa了,即对于点和路比较多的那种用spfa,点比较少的用普通的dij和floyd就行.)

#include
#include
#include
#include
#include
#include
#include
#define CLR(x) memset(x,0,sizeof(x))
#define ll long long int
using namespace std;
const int maxn=1e6+5;
const ll inf=1e15;      //这个inf必须足够大才行,否则会一直WA.!!!这是坑点,坑了我好久.
int head[maxn];
int weight[maxn];
ll dis[maxn],vis[maxn];
int n,m,ans;
struct node
{
    int to;
    int v;
    int next;
}edge[maxn];    //边跟点的范围要分清楚,不要开成一样的了,否则会WA!!!

void add(int a,int b,int v)
{
    edge[ans].to=b;   //a可以到b点.
    edge[ans].v=v;    //v是两点间的距离.
    edge[ans].next=head[a];    //结束标记,表示没有再于a点相连的点了.
    head[a]=ans++;
}

void spfa(int u)
{
    queueq;
    dis[u]=0;
    vis[u]=1;
    q.push(u);
    while(!q.empty()){
        int k=q.front();   //从不断入队中的元素中不断进行循环的那个步骤,直到队列为空.
        //printf("%d\n",k);
        q.pop();
        vis[k]=0;
        for(int i=head[k];i!=-1;i=edge[i].next)//懂不起写出来就可以了.
        {          //headx表示提取与x直接相连的点的信息.然后直到提取到head为-1时(初始化值),表示提取完毕,即结束.(然后进行下一个开头的提取.)
            int m=edge[i].to;
            if(dis[m]>dis[k]+edge[i].v){//意思是u点到m点的距离如果大于u点到k点再到m点的距离的话.
                dis[m]=dis[k]+edge[i].v;
                if(!vis[m]){
                    vis[m]=1;
                    q.push(m);
                }
            }
        }
    }
}

void solve()
{
    CLR(vis);
    memset(head,-1,sizeof(head));
    ans=0;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        dis[i]=inf;
        scanf("%d",&weight[i]);
    }
    for(int i=0;i

你可能感兴趣的:(最短路算法)