最短路(模板)
hdu2544模板题目:http://acm.hdu.edu.cn/showproblem.php?pid=2544
迪杰斯特拉
初级版:邻接数组表示(和Prim极为相似!)(无向图即双向的)
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <iomanip> 5 #include <cstdio> 6 #include <cstring> 7 using namespace std; 8 const int maxn=1e4+5; 9 const int maxm=1e6+5; 10 const int inf=1e9; 11 int dist[maxn],vis[maxn]; 12 int Map[5005][5005]; 13 int n,m; 14 int ans; 15 16 void Init() 17 { 18 for(int i=1;i<=n;i++)//初始化边权值 19 { 20 for(int j=1;j<=n;j++) 21 { 22 if(i==j) Map[i][j]=0; 23 else Map[i][j]=inf; 24 } 25 } 26 for(int i=1;i<=n;i++) dist[i]=inf;//初始化最小权值 27 memset(vis,0,sizeof(vis));//初始化访问记录 28 } 29 30 void Dijistra(int x) 31 { 32 int temp,lowcast;//记录最小权值顶点和最小权值 33 34 for(int i=1;i<=n;i++) dist[i]=Map[x][i];//与该集合内所有连接点的最小权值 35 //dist[s]=0;//源点为0 36 vis[x]=1; 37 for(int i=1;i<=n-1;i++)//n-1条边即可退出(到达其余n-1个点) 38 { 39 lowcast=inf; 40 for(int j=1;j<=n;j++)//循环一论,找出所有连接点中的最小权值 41 { 42 if(!vis[j] && dist[j]<lowcast) 43 { 44 lowcast=dist[j]; 45 temp=j; 46 } 47 } 48 49 vis[temp]=1;//更新集合,加入点 50 //ans+=lowcast;没用,Prim会用到,应该是if(lowcast>=inf) break; 51 52 for(int j=1;j<=n;j++) if(!vis[j] && dist[j]>dist[temp]+Map[temp][j]) dist[j]=dist[temp]+Map[temp][j];//根据新加入的点更新所有连接点的最小权值 53 } 54 55 } 56 57 int main() 58 { 59 ios::sync_with_stdio(false); cin.tie(0); 60 61 while(cin>>n>>m && n && m) 62 { 63 Init(); 64 for(int i=1;i<=m;i++) 65 { 66 int x,y,z; 67 cin>>x>>y>>z; 68 69 if(z<Map[x][y]) 70 { 71 Map[x][y]=z; 72 Map[y][x]=z; 73 } 74 } 75 76 Dijistra(1); 77 78 cout<<dist[n]<<endl; 79 } 80 81 return 0; 82 }
中级版:链式前向星
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <iomanip> 5 #include <cstdio> 6 #include <cstring> 7 using namespace std; 8 const int maxn=1e5+5;//最大点数 9 const int maxm=1e6+5;//最大边数 10 const int inf=2147483647; 11 int dist[maxn],vis[maxn],head[maxn]; 12 //int Map[maxn][maxn]; 13 int n,m,s;//顶点数,边数,出发点 14 int ans,cnt;//记录和Prim会用到,所有边数量 15 struct px 16 { 17 int next; 18 int to; 19 int w; 20 }T[maxm*3];//注意T存的是所有边数(边的连接信息,终点,权值,下条边都在这里),无向图是2倍 21 22 23 void Init() 24 { 25 for(int i=1;i<=n;i++) dist[i]=inf;//初始化最小权值 26 memset(vis,0,sizeof(vis));//初始化访问记录 27 } 28 29 void Add(int x,int y,int z) 30 { 31 T[++cnt].next=head[x]; 32 T[cnt].to=y; 33 T[cnt].w=z; 34 head[x]=cnt; 35 } 36 37 void Dijistra(int x) 38 { 39 int temp,lowcast;//记录最小权值顶点和最小权值 40 41 //本意就是找与该集合内所有连接点的最小权值,两种方式都可以,第2种更好(链式存储) 42 //for(int i=1;i<=n;i++) dist[i]=Map[x][i];//不管连接与否,找到一遍(没连接的为inf) 43 for(int i=head[x];i;i=T[i].next)//只找连接的,没连接的直接不找(dist事先初始化inf效果一样) 44 { 45 if(T[i].w<dist[T[i].to])//注意重边情况! 46 { 47 dist[T[i].to]=T[i].w; 48 } 49 } 50 51 dist[x]=0; 52 vis[x]=1; 53 for(int i=1;i<=n-1;i++)//n-1条边即可退出 54 { 55 lowcast=inf; 56 for(int j=1;j<=n;j++)//循环一论,找出所有连接点中的最小权值 57 { 58 if(!vis[j] && dist[j]<lowcast) 59 { 60 lowcast=dist[j]; 61 temp=j; 62 } 63 } 64 65 vis[temp]=1;//更新集合,加入点 66 //ans+=lowcast;Prim会用到 67 if(ans>=inf) break;//说明没有点连接,不连通,退出 68 69 //for(int j=1;j<=n;j++) if(!vis[j] && dist[j]>Map[temp][j]) dist[j]=Map[temp][j];//根据新加入的点更新所有连接点的最小权值 70 for(int j=head[temp];j;j=T[j].next) 71 { 72 int J=T[j].to;//注意此J非彼j(J才是连接点终点(没有到达的点),j存放连接点的结构体序号位置) 73 if(!vis[J] && dist[J]>dist[temp]+T[j].w) 74 { 75 dist[J]=dist[temp]+T[j].w; 76 } 77 } 78 } 79 } 80 81 int main() 82 { 83 ios::sync_with_stdio(false); cin.tie(0); 84 85 cin>>n>>m>>s; 86 Init();//注意初始化无限大必须放在输入之前! 87 for(int i=1;i<=m;i++) 88 { 89 int x,y,z; 90 cin>>x>>y>>z; 91 92 Add(x,y,z); 93 //Add(y,x,z);有向图(单向的) 94 } 95 96 Dijistra(s); 97 98 for(int i=1;i<=n-1;i++) cout<<dist[i]<<' '; 99 cout<<dist[n]<<endl; 100 101 return 0; 102 }
高级版:链式前向星+堆优化(其实不管是否优化,最开始的源点操作放在循环里与循坏外都是可以行得通!只不过优化前放到外面是初学为了逻辑更清晰先把源点放进去再操作n-1次;优化后放到循环内是为了方便统一理解操作,此时优化已经建立在逻辑清晰的基础上了!)
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <iomanip> 5 #include <queue> 6 #include <cstdio> 7 #include <cstring> 8 using namespace std; 9 const int maxn=1e4+5;//最大点数 10 const int maxm=1e5*2;//最大边数 11 const int inf=2147483647; 12 int dist[maxn],vis[maxn],head[maxn]; 13 //int Map[maxn][maxn]; 14 int n,m,s;//顶点数,边数,源点 15 int ans,cnt,Cnt;//记录和,所有边数量 16 //ans,Cnt,Prim会用到 17 struct px 18 { 19 int next; 20 int to; 21 int w; 22 }T[maxm*3];//注意T存的是所有边数(边的连接信息,终点,权值,下条边都在这里),无向图是2倍 23 struct pxx 24 { 25 int j; 26 int dist; 27 bool operator<(const pxx &a) const 28 { 29 return dist>a.dist; 30 } 31 }Tmp; 32 priority_queue<pxx> que; 33 34 void Init() 35 { 36 for(int i=1;i<=n;i++) dist[i]=inf;//初始化最小权值 37 memset(vis,0,sizeof(vis));//初始化访问记录 38 } 39 40 void Add(int x,int y,int z) 41 { 42 T[++cnt].next=head[x]; 43 T[cnt].to=y; 44 T[cnt].w=z; 45 head[x]=cnt; 46 } 47 48 void Dijkstra(int x) 49 { 50 dist[x]=0;//不能少,且vis[x]=1不能要(因为要在循环里操作) 51 Tmp.j=x; 52 Tmp.dist=0; 53 que.push(Tmp); 54 55 while(!que.empty())//循环从s开始操作(区别没优化时:s在循环外操作,从s的邻接点开始循环操作) 56 { 57 pxx tmp=que.top(); 58 que.pop(); 59 if(vis[tmp.j]) continue; 60 61 vis[tmp.j]=1; 62 //ans+=tmp.dist;//Prim会用到 63 //Cnt++;//和没优化时区别,记录点数,Prim用到 64 for(int j=head[tmp.j];j;j=T[j].next) 65 { 66 int J=T[j].to;//注意此J非彼j(J才是连接点终点(没有到达的点),j存放连接点的结构体序号位置) 67 if(dist[J]>dist[tmp.j]+T[j].w) 68 { 69 dist[J]=dist[tmp.j]+T[j].w; 70 Tmp.j=J; 71 Tmp.dist=dist[J]; 72 que.push(Tmp); 73 } 74 } 75 } 76 } 77 78 int main() 79 { 80 ios::sync_with_stdio(false); cin.tie(0); 81 82 cin>>n>>m>>s; 83 Init();//注意初始化无限大必须放在输入之前! 84 for(int i=1;i<=m;i++) 85 { 86 int x,y,z; 87 cin>>x>>y>>z; 88 89 Add(x,y,z); 90 //Add(y,x,z); 91 } 92 93 Dijkstra(s); 94 95 for(int i=1;i<=n-1;i++) cout<<dist[i]<<' '; 96 cout<<dist[n]<<endl; 97 98 return 0; 99 }
链式前向星改为vector
vector+堆优化
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <iomanip> 5 #include <vector> 6 #include <queue> 7 #include <cstdio> 8 #include <cstring> 9 using namespace std; 10 const int maxn=1e4+5;//最大点数 11 const int maxm=1e6+5;//最大边数 12 const int inf=2147483647; 13 int dist[maxn],vis[maxn]; 14 //int Map[maxn][maxn]; 15 int n,m,s;//顶点数,边数,源点 16 int ans,cnt,Cnt;//记录和,所有边数量 17 //ans,Cnt都是Prim会用到 18 struct px 19 { 20 int to; 21 int w; 22 }T; 23 vector<px> vec[maxn]; 24 struct pxx 25 { 26 int j; 27 int dist; 28 bool operator<(const pxx &a) const 29 { 30 return dist>a.dist; 31 } 32 }Tmp; 33 priority_queue<pxx> que; 34 35 void Init() 36 { 37 for(int i=1;i<=n;i++) dist[i]=inf;//初始化最小权值 38 memset(vis,0,sizeof(vis));//初始化访问记录 39 } 40 41 void Dijkstra(int x) 42 { 43 dist[x]=0;//不能少,且vis[x]=1不能要(因为要在循环里操作) 44 Tmp.j=x; 45 Tmp.dist=0; 46 que.push(Tmp); 47 48 while(!que.empty())//循环从s开始操作(区别没优化时:s在循环外操作,从s的邻接点开始循环操作) 49 { 50 pxx tmp=que.top();//重边时也考虑到了,自动取得最小值 51 que.pop(); 52 if(vis[tmp.j]) continue;//重点重边时也考虑到了,返回 53 54 vis[tmp.j]=1; 55 //ans+=tmp.dist;//Prim会用到 56 for(int j=0;j<vec[tmp.j].size();j++) 57 { 58 int J=vec[tmp.j][j].to,W=vec[tmp.j][j].w; 59 if(!vis[J] && dist[J]>dist[tmp.j]+W) 60 { 61 dist[J]=dist[tmp.j]+W; 62 Tmp.j=J; 63 Tmp.dist=dist[J]; 64 que.push(Tmp); 65 } 66 } 67 68 //和没优化时区别,必须放在for循环后退出,不然dist[n]可能没更新到 69 //if(Cnt==n) break;//n个点最短距离都求过了可以退出 70 } 71 } 72 73 int main() 74 { 75 ios::sync_with_stdio(false); cin.tie(0); 76 77 cin>>n>>m>>s; 78 Init();//注意初始化无限大必须放在输入之前! 79 for(int i=1;i<=m;i++) 80 { 81 int x,y,z; 82 cin>>x>>y>>z; 83 84 T.to=y; 85 T.w=z; 86 vec[x].push_back(T);//单向图 87 } 88 89 Dijkstra(s); 90 91 for(int i=1;i<=n-1;i++) cout<<dist[i]<<' '; 92 cout<<dist[n]<<endl; 93 94 return 0; 95 }
一般情况下先vector好理解,一般够用了,不行了再换链式前向星!
//18.11.07,最新理解+最新模板
1.dijkstra就是构造一个最有解集合,不断把(它相连的点)点更新加入最优集合,直到最优集合有了n个点即==原集合,此时到所有点的最短距离就求出来了!(关于这一点是break还是遍历完的问题,我认为:此时可以退出了,已经放入最优集合中n个点了,都是最优解了就算还有边也不是最小的最优的)
2.当边的权值都为1时,优先队列就变成了队列,此时就是BFS,即BFS本质是dijkstra!(只不过BFS不一定是到n点,到中间任何目标一点到达后就可以退出了)
3.结构体存东西,写出构造方法会非常方便,不用再定义临时变量直接放!
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <iomanip> 5 #include <vector> 6 #include <queue> 7 #include <cstdio> 8 #include <cstring> 9 using namespace std; 10 const int maxn=1e4+5;//最大点数 11 const int maxm=1e6+5;//最大边数 12 //const int inf=2147483647;//再加就变成负了不好 13 const int inf=2100000000; 14 int dist[maxn],vis[maxn]; 15 //int Map[maxn][maxn]; 16 int n,m,s;//顶点数,边数,源点 17 int ans,cntn;//记录和,已放入最有集合的边数量 18 //ans,Prim会用到 19 struct px//存图结点信息 20 { 21 int to; 22 int w; 23 px(){}; 24 px(int TO,int W):to(TO),w(W){}; 25 }; 26 vector<px> vec[maxn]; 27 struct pxx//存所有相连的边的信息 28 { 29 int j; 30 int dist; 31 bool operator<(const pxx &a) const 32 { 33 return dist>a.dist; 34 } 35 pxx(){}; 36 pxx(int J,int DIST):j(J),dist(DIST){}; 37 }; 38 priority_queue<pxx> que; 39 40 void Init() 41 { 42 for(int i=1;i<=n;i++) dist[i]=inf;//初始化最小权值 43 memset(vis,0,sizeof(vis));//初始化访问记录 44 } 45 46 void Dijkstra(int x) 47 { 48 dist[x]=0;//不能少,且vis[x]=1不能要(因为要在循环里操作) 49 que.push(pxx(x,0)); 50 //只要非空就一直操作比判断inf更好(比如1没有相连的边下面就不加入集合直接退出) 51 while(!que.empty())//循环从s开始操作(区别没优化时:s在循环外操作,从s的邻接点开始循环操作) 52 { 53 pxx tmp=que.top();//重边时也考虑到了,自动取得最小值 54 que.pop(); 55 if(vis[tmp.j]) continue;//重点重边时也考虑到了,返回 56 57 vis[tmp.j]=1; 58 //ans+=tmp.dist;//Prim会用到 59 cntn++; 60 if(cntn==n) break;//此时可以退出了,已经放入最优集合中n个点了,都是最优解了就算还有边也不是最小的最优的 61 62 for(int j=0;j<vec[tmp.j].size();j++) 63 { 64 int J=vec[tmp.j][j].to,W=vec[tmp.j][j].w; 65 if(!vis[J])//最优集合中的就不说了一定是最优,判断不是最优集合的进行更新就行了 66 { 67 if(dist[J]>dist[tmp.j]+W) 68 { 69 dist[J]=dist[tmp.j]+W; 70 que.push(pxx(J,dist[J])); 71 } 72 } 73 } 74 } 75 } 76 77 int main() 78 { 79 ios::sync_with_stdio(false); cin.tie(0); 80 81 while(cin>>n>>m) 82 { 83 if(n==0 && m==0) break; 84 85 Init();//注意初始化无限大必须放在输入之前! 86 cntn=0;//多组输入时也要初始化 87 for(int i=1;i<=m;i++) 88 { 89 int x,y,z; 90 cin>>x>>y>>z; 91 92 vec[x].push_back(px(y,z));//单向图 93 vec[y].push_back(px(x,z));//双向图 94 } 95 96 Dijkstra(1); 97 98 //for(int i=1;i<=n-1;i++) cout<<dist[i]<<' '; 99 cout<<dist[n]<<endl; 100 101 while(que.size()) que.pop();//多组输入时也要清空 102 for(int i=1;i<=n;i++) vec[i].clear(); 103 } 104 105 return 0; 106 }
完。