学习篇
求最短路径的基本思想:
按照最短路径的长度递增的次序,依次求得——源点到其余各点的最短路径!
假设,从源点到顶点v的最短路径是所有最短路径中长度最短者。
路径长度 最短的最短路径 的特点:
在这条路径上,必定只含有一条弧,并且这一条弧的权值最小。
简单来说:
一条最短路径比如:Start -> a->····-> End
在一个图中,找到与源点距离最短的第一个点①,即找到第一个最短路,
再找与源点距离第二短的点②,此时寻找的第二个点有两种方式,要么是源点直接到这点(前提是源点与这个点有连接),要么是经过①到这个点(前提是①与这个点有连接),则源点到该点的最短路为以上两种方式的最小值;
以此类推
找到与源点第x短的点,若此时的点为终点,则最短路径长度为x
举个例子:
迪杰斯特拉Dijkdtra算法:
假设s为刚刚求得的最短路节点,则:
Dist[k]=min( Dist[k] , Map[s][k] + Dist[s] );
邻接矩阵做法:请看 hdu 1874
堆优化做法:待补
注意:
有向和无向:题目一般会在 输入格式中的输入 a,b两城市的后面补充这样的语句
有向:(a way)
无向:(a,b之间有多条路)(a,b之间是一条双向道路)
水题集
hdu 1874:畅通工程续-单源点最短路
#include <bits/stdc++.h>
#define ll long long
#define inf 0x7FFFFFFF
using namespace std;
//dis[i]=min(dis[i],dis[s]+Map[s][i]);
int Map[210][210],v[210],dis[210];
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,m;
while(cin>>n>>m){
//初始化
for(int i=0;i<n;i++){
dis[i]=inf;v[i]=0;
for(int j=0;j<n;j++){
Map[i][j]=inf;//i==j的情况为什么不是0,看看状态转移方程想想为什么
}
}
//填表
for(int i=0;i<m;i++){
int a,b,c;cin>>a>>b>>c;
Map[b][a]=Map[a][b]=min(Map[a][b],c);//去重边!!
}
int s,e;cin>>s>>e;dis[s]=0;v[s]=1;//记得更新起点的状态(v dis)
while(s!=e){
int next,mi=inf;
for(int i=0;i<n;i++){//遍历不是“起点”的点
if(!v[i]){
if(Map[s][i]!=inf)
dis[i]=min(dis[i],dis[s]+Map[s][i]);更新状态 (dis)
if(dis[i]<mi){mi=dis[i];next=i;}//找最小值即下一个“起点”
}
}
if(mi==inf)break;//没有路到终点了
s=next;
v[s]=1;//记得更新“起点”状态(v)
}
if(dis[e]==inf)cout<<"-1"<<endl;
else cout<<dis[e]<<endl;
}
return 0;
}
hdu 2066:多源点
hdu 2066
多源点转化成单源点:
构造一个虚拟源点,再手动连接一下虚拟源点与题中的多起/终点;
#include <bits/stdc++.h>
#define ll long long
#define inf 0x7FFFFFFF
using namespace std;
//dis[i]=min(dis[i],dis[s]+Map[s][i]);
int Map[1010][1010],v[1010],dis[1010];
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int T,S,D;
while(cin>>T>>S>>D){
for(int i=0;i<1010;i++){
dis[i]=inf;v[i]=0;
for(int j=0;j<1010;j++)
Map[i][j]=inf;
}
int n=0;
while(T--){
int a,b,c;cin>>a>>b>>c;
Map[b][a]=Map[a][b]=min(Map[a][b],c);
n=max(n,max(a,b));
}
n++;
while(S--){
int st;cin>>st;
Map[0][st]=Map[st][0]=0;
}
while(D--){
int ed;cin>>ed;
Map[ed][n]=Map[n][ed]=0;
}
int s=0,e=n;v[s]=1;dis[s]=0;
while(s!=e){
int mi=inf,next;
for(int i=0;i<=n;i++){
if(!v[i]){
if(Map[s][i]!=inf)
dis[i]=min(dis[i],dis[s]+Map[s][i]);
if(mi>dis[i]){mi=dis[i];next=i;}
}
}
if(mi==inf)break;
s=next;
v[s]=1;
}
cout<<dis[e]<<endl;
}
return 0;
}
hdu 2680:多源点&&有向
这个题wa了一次:在于没搞清题目怎样才算是有向还是无向,就顺势搞清了
题中“a way”指明是有向
#include <bits/stdc++.h>
#define ll long long
#define inf 0x7FFFFFFF
using namespace std;
//dis[i]=min(dis[i],dis[s]+Map[s][i]);
int Map[1010][1010],v[1010],dis[1010];
int main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,m,e;
while(cin>>n>>m>>e){
for(int i=0;i<=n;i++){
dis[i]=inf;v[i]=0;
for(int j=0;j<=n;j++)
Map[i][j]=inf;
}
while(m--){
int a,b,c;cin>>a>>b>>c;
Map[a][b]=min(Map[a][b],c);
}
int w;cin>>w;
while(w--){
int ed;cin>>ed;
Map[0][ed]=0;
}
int s=0;v[s]=1;dis[s]=0;
while(s!=e){
int mi=inf,next;
for(int i=0;i<=n;i++){
if(!v[i]){
if(Map[s][i]!=inf)
dis[i]=min(dis[i],dis[s]+Map[s][i]);
if(mi>dis[i]){next=i;mi=dis[i];}
}
}
if(mi==inf)break;
s=next;
v[s]=1;
}
if(dis[e]==inf)cout<<"-1"<<endl;
else cout<<dis[e]<<endl;
}
return 0;
}