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]记录的就是最短的路径
这是最普通的模板 之后会一点点复杂化
先贴上基本的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()操作来实现
方法: 先用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;
}