最短路径 dijkstra算法实现问题总结

dijkstra算法解决的是单源的最短路径问题 一般输出给定起点到其余各点的最短距离 最短的路径用pre记录前驱 用递归的方法求最短路径
在权值非负时有效
首先 给出dijkstra算法的伪代码

初始化起点 d[s]=0 ,其余为inf
for( int i=0;i<N;i++)  //有n个结点n循环n次  每次加入一个结点
{
	int u=-1,min=inf;
	for(int j=0;i<n;j++){
		找到未访问结点中  d[u]最小的那个
	}
	if(u== -1)找不到 return;
	找到 vis[u]=true;
	if(以u为中间结点  优化u所连的所有未访问的结点)
	{优化}

最后的d[i]记录的就是最短的路径

模板1

这是最普通的模板 之后会一点点复杂化
先贴上基本的dijkstra的代码
假设无向图已经建立好 采用邻接矩阵存

void dijkstra(int s)
{
    fill(d,d+maxn,inf);
    d[s]=0;
    for(int i=0;i<n;i++)
    {
        int u=-1;int min=inf;
        for(int j=0;j<n;j++)
        {
            if(vis[j]==false && d[j]<min){//找到未访问里面的最小的  访问他
                u=j;
                min=d[j];
            }
        }
        if(u ==-1) return ;// 未找到证明没有连通和i连通的了
        vis[u]=true;
        for(int v=0;v<n;v++){  //以u为中介  优化未访问的结点
            if(vis[v] == false && G[u][v] != inf  &&d[u]+ G[u][v] <d[v])
            {
                d[v]=d[u]+G[u][v];
            }
    }
}

下面进行复杂化的模板 直接在上面的基础上更改
可能会增加以下的条件
① 最短的路径不止一条 输出所有条数
②在①的条件下 给每条边加边权(如花费 时间)优先输出边权最小的或最大的
加点权 每个点(人数 等) 输出最多的人数 等
③ 问有多少条最短路径
方法: 加边权,点权的数组 d【】 w【】 num’【】 d【】是距离 一般就是边权
只要在更新 d【v】的时候 加上即可 其余的不用改

上代码
原题 patA 1003 题目

//首先定义变量名  初始化
const int inf=100000000;
int G[maxn][maxn];
int d[maxn], w[maxn], num[maxn]; //d最短距离 w最大点权和  num最短路劲数
int n,m,st,ed,weight[maxn];

bool vis[maxn]={false};
void dijkstra(int s)
{
    fill(d,d+maxn,inf);
    memset(w,0,sizeof(d));  //点权之和为零  救助人数
    d[s]=0;
    w[s]=weight[s];
    num[s]=1;
    for(int i=0;i<n;i++)
    {
        int u=-1;int min=inf;
        for(int j=0;j<n;j++)
        {
            if(vis[j]==false && d[j]<min){//找到未访问里面的最小的  用于加入已访问
                u=j;
                min=d[j];
            }
        }
        if(u ==-1) return ;
        vis[u]=true;
        for(int v=0;v<n;v++){
            if(vis[v] == false && G[u][v] != inf &&d[u]+ G[u][v] <d[v])  //可优化的直接优化
            {
                d[v]=d[u]+G[u][v];
                w[v]=w[u]+weight[v];
                num[v]=num[u];
            }
            else if(d[u]+G[u][v] == d[v]){  //注意  先在相等的条件下  更新数量  优先点权大的
                if(w[u] +weight[v] >w[v]){  //优先点权大的
                    w[v]=w[u]+weight[v];
                }
                num[v]+=num[u];  //更新相等路径数量
            } 
        }
    }
}

最后在num【i】 的d【i】 w【】中的就是源点到i的路径数 最短距离 最?值 直接输出即可

总结 所有简单的对边 权值求和的需求都可以用上面的模板进行 但是在对逻辑更复杂的可能就存在问题 ,如果不能简单相加的话 就要用别的方法来解决 先用dijkstra求出最短的路径 (注意 不是距离) + dfs()操作来实现

模板 2 适用于更复杂的一般情况

方法: 先用dijkstra算法记录最短的路径 然后再这些最短的路径中选择一条第二标尺(就是题目给定的限定条件)最优的路线,因为在给定路线时,所有和路线相关的信息都可以比较容易的计算出来
考虑到相等的最短路径的情况 之前用的pre【】记录单个前驱结点就不够用了 所以用 vector <> pre;
在获得 pre【v】时 如果是可以优化的 那就必须先pre【v】.clear() 因为之前的是可优化的 那就不是最短的路径了 (舍弃)
如果是相等的 那就直接加入到pre中就好了

所以此时的dijkstra()代码如下

vector  <int> pre;
int inf=1000000000;
void dijkstra(int s)
{
	fill(d,d+maxn,inf);
	d[s]=0;
	for(int i=0;i<n;i++){
		int u=-1,min=inf;
		for(int j=0;j<n;j++{
			if(vis[j]==false && d[j]<min)
			{
				u=j;
				min=d[j];
			}
		}
		if(u==-1) return;
		vis[u]=true;
		for(int v=0;v<n;v++)
		{
			if(vis[v] == false && G[u][v] !=inf && d[u]+G[u][v]<d[v])  //可以优化的
			{
				d[v]=d[u]+G[u][v];
				pre[v].clear();   //和之前的主要的差别
				pre[v].push_back(u);
			} 
			else if(d[u]+G[u][v]== d [v])
			{
				pre[v].push_back(u);
			}
		}
	}
}

二· 完成上一步之后 便是遍历所有的最短的路径 找出最优的那一条 采用dfs递归 因为pre到路径 用dfs
到最后叶子结点的时候 就是一条路径 求每一个路径,记录用ans 遍历下一个时 和ans比较 更优则更新即可 最后输出
需要输出路径的话 就用2个数字记录 一个临时 一个最优数组
代码如下

int bestvalue;  //最优值
vector <int> pre[maxn];
vector <int> path, temppath;
void dfs(int v)//v是当前访问结点
//输入递归边界

if(v==st) //到达起点  st是起点
{
	temppath.push_back(v);
	numpath++; //最短路径数加1  初值为零
	int value;//存放临时路径的ans值
	for(int i=temppath.size() -1;i>=0;i--)
	{
		计算临时路径值 ;
	}
	if(比较 2){
		最优则更新path和 bestvalue
	}
	elses  if(在一条件相等时,在有第二判断条件下加入)
	{
		//第二条件并更新
	}

	temppath.pop_back();//将刚加入的结点  删除
	return}
//以下的语句一般都是模板化的  是不需要更该的
temppath.push_back(v);//不是起点  把v放入路径中  
for(int i=0;i<pre[v].size();i++)
{
	dfs(pre[v][i]);
}
temppath.pop_back(); //遍历完所有的前去  删除v   思考为什么????

注意在上面的代码中 在·path路径中的结点的顺序是逆序的

patA 1030 题目

代码如下 2种方法均可

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int maxn=505;
const int inf=1000000000;
注释的是第一种方法
/*
int n,m,st,ed,G[maxn][maxn],cost[maxn][maxn];  //距离是1  但是每边的权值不一样,有些cost高  
int d[maxn],c[maxn],pre[maxn];
bool vis[maxn]={false};

void dijkstra(int s) // s是起点
{
    fill(d,d+maxn,inf);
    fill(c,c+maxn,inf);
    for(int i=0;id[u]+G[u][v] && G[u][v] != inf)//优化
            {
                pre[v]=u;
                d[v]=d[u]+G[u][v];
                c[v]=c[u]+cost[u][v];
            }
            else if(d[v] == d[u]+G[u][v])
            {
                if(c[u]+cost[u][v]< c[v])
                {
                    c[v]=c[u]+ cost[u][v];
                    pre[v]=u;
                }
            }
        }
    }
}

void dfs(int v)//v是当前访问结点
{
    if(v == st)
    {
        printf("%d ",v);
        return;
    }
    dfs(pre[v]);
    printf("%d ",v);
}




int main()
{
    scanf("%d%d%d%d",&n,&m,&st,&ed);
    int u,v;
    fill(G[0],G[0]+maxn*maxn,inf);
    fill(cost[0],cost[0]+maxn*maxn,inf);
    for(int i=0;i
//以上的代码为第一个模板的方法  

int n,m,st,ed,G[maxn][maxn],cost[maxn][maxn];  //距离是1  但是每边的权值不一样,有些cost高  
int d[maxn],mincost=inf;
bool vis[maxn]={false};
vector <int> pre[maxn];
vector <int > path,temppath;
void dijkstra(int s) // s是起点
{
    fill(d,d+maxn,inf);
    d[s]=0;
    for(int i=0;i<n;++i)
    {
        int u=-1,min=inf;
        for(int j=0;j<n;++j)
        {
            if(vis[j] == false && d[j]<min )
            {
                u=j;
                min=d[j];
            }
        }
        if(u == -1) return;
        vis[u] = true;
        for(int v=0;v<n;++v)
        {
            if(vis[v]== false && d[v]>d[u]+G[u][v] && G[u][v] != inf)//优化
            {
                pre[v].clear();
                pre[v].push_back(u);
                d[v]=d[u]+G[u][v];
            }
            else if(d[v] == d[u]+G[u][v])
            {
                pre[v].push_back(u);
            }
        }
    }
}

void dfs(int v)
{
    if(v == st)
    {
        temppath.push_back(v);
        int tempcost=0;
        for(int i=temppath.size() - 1;i>0;i--)//到着访问
        {
            int id=temppath[i],nextid=temppath[i-1];
            tempcost+= cost[id][nextid];
        }
        if(tempcost < mincost)
        {
            mincost=tempcost;
            path=temppath;
        }
        temppath.pop_back();
        return;
    }
    temppath.push_back(v);
    for(int i=0;i<pre[v].size();i++)
    {
        dfs(pre[v][i]);
    }
    temppath.pop_back();
}

int main()
{
    scanf("%d%d%d%d",&n,&m,&st,&ed);
    int u,v;
    fill(G[0],G[0]+maxn*maxn,inf);
    fill(cost[0],cost[0]+maxn*maxn,inf);
    for(int i=0;i<m;++i)
    {
        scanf("%d%d",&u,&v);
        scanf("%d%d",&G[u][v],&cost[u][v]);
        cost [v][u]=cost[u][v];
        G[v][u]=G[u][v];
    }
    dijkstra(st);
    dfs(ed);  //打印路劲
    for(int i=path.size()-1;i>=0;i--)
    {
        printf("%d ",path[i]);
    }
    printf("最小%d 花费%d\n",d[ed],mincost);
    system("pause");
    return 0;
}


你可能感兴趣的:(最短路径 dijkstra算法实现问题总结)