1. Dijkstra算法
只能计算权重为正的情况,如有重边取最小值作为边,输出路径用dfs。
# include<iostream>
# include<cstring>
using namespace std;
int n,m;
const int N=510,M=100010;
int g[N][N],used[N],d[N],pre[N];
int Dijkstra(){
d[1]=0;
//找v-s中距离最小的点加入s,进行n-1次操作
for(int i=0;i<n;i++){
int t=-1;
//找最小的点
for(int j=1;j<=n;j++){
if(!used[j]&&(t==-1||d[t]>d[j])){
t=j;
}
}
//加入s
used[t]=1;
//更新距离
for(int j=1;j<=n;j++){
if(!used[j]&&d[t]+g[t][j]<d[j])
{
d[j]=d[t]+g[t][j];
//记录前驱节点
pre[j]=t;
}
}
}
if(d[n]==0x3f3f3f3f) return -1; //注意是4个3f
else return d[n];
}
//输出路径
void dfs(int s,int d){
// 如果已经到达起点,则返回起点在返回
if(s==d){
printf("%d ",s);
return;
}
dfs(s,pre[d]);
printf("%d ",d);//从最深处回来之后,输出每一层的顶点号
}
int main(){
// ios::sync_with_stdio(false);
// cin.tie(0);
cin>>n>>m;
memset(d,0x3f,sizeof d);
memset(g,0x3f,sizeof g);
while(m--){
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c); //处理重边
}
printf("%d\n",Dijkstra());
//输出路径
cout<<"---------"<<endl;
dfs(1,n);
return 0;
}
Dijkstra算法的变种
如果有多条最短路,可能会出现如下的变种:
- 给每条边加上花费,要求最短路并且花费最少;
- 给每个点加上报酬,要求经过的点报酬最多;
- 直接问有几条最短路。
对于第一种情况:设置数组cost[u][v]
表示u->v
的花费,令从起点s到终点u的最小花费为 c[u]
,只需改变下面几行代码:
for(int j=1;j<=n;j++){
if(!used[j])
{
if(d[j]>d[t]+g[t][j]){//距离不相同,说明路径唯一
c[j]=cost[t][j]+c[t];
d[j]=d[t]+g[t][j];
}else if(d[j]==d[t]+g[t][j]&&c[j]>cost[t][j]+c[t])
{//距离相同,说明路径不唯一,更新最小花费
c[j]=cost[t][j]+c[t];
}
}
}
对于第二种情况: 其实这种情况和第一种情况差不多,设置weight[u]
数组表示节点u的报酬,令从起点s到终点u的最大报酬为w[u]
,代码如下:
for(int j=1;j<=n;j++){
if(!used[j])
{
if(d[j]>d[t]+g[t][j]){//距离不相同,说明路径唯一
w[j]=weight[j]+w[t];
d[j]=d[t]+g[t][j];
}else if(d[j]==d[t]+g[t][j]&&w[j]<weight[j]+w[t])
{//距离相同,说明路径不唯一,更新最大报酬
w[j]=weight[j]+w[t];
}
}
}
对于第三种情况:只需要增加一个数组num[]
,令从起点s到终点u的最短路径条数为num[u]
。初始化时只有num[s]=1,其余都为0,相关代码为:
int num[N];
memset(num,0,sizeof num);
num[1]=1;
for(int j=1;j<=n;j++){
if(!used[j])
{
if(d[j]>d[t]+g[t][j]){//距离不相同,说明路径唯一
w[j]=weight[j]+w[t];
num[j]=num[t];
}else if(d[j]==d[t]+g[t][j]&&w[j]<weight[j]+w[t])
{//距离相同,说明路径不唯一,更新最短路径条数
num[j]+=num[t]; //...t->j的最短路径有num[t]条,...其他->j的有num[j]
}
}
}
}
将上面的知识点总结起来的题为:【PAT A1003】Emergency
# include<cstring>
# include<iostream>
# include<algorithm>
using namespace std;
const int N=510;
int n,m,s,v;
int d[N],g[N][N],weight[N],w[N],used[N],num[N];
int i,j;
void Dijkstra()
{
d[s]=0;
num[s]=1;
w[s]=weight[s];
for(i=0;i<n;i++)
{
int t=-1,MIN=0x3f3f3f3f;
for(j=0;j<n;j++)
{
//找到最短点
if(!used[j]&&d[j]<MIN){
t=j;
MIN=d[j];
}
}
used[t]=1;
//更新
for(j=0;j<n;j++){
if(!used[j]){
if(d[j]>d[t]+g[t][j]){d[j]=d[t]+g[t][j]; w[j]=w[t]+weight[j];num[j]=num[t];}
else if(d[j]==d[t]+g[t][j])
{
if(w[j]<w[t]+weight[j]){w[j]=w[t]+weight[j];}//最大报酬
num[j]+=num[t];
}
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n>>m>>s>>v;
memset(d,0x3f,sizeof d);
memset(g,0x3f,sizeof g);
for(i=0;i<n;i++) cin>>weight[i];
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=g[b][a]=min(g[a][b],c);
}
Dijkstra();
cout<<num[v]<<' '<<w[v]<<endl;
return 0;
}