觉得这篇博文讲的很不错:Click here
下面的代码可直接用来AC这道题目:hdu-畅通工程续(click here)
One(邻接矩阵 时间复杂度o(n^2))
一个用邻接矩阵存图的模板。邻接矩阵存图主要用于点比较少的(5000个点还是可以的),边比较多的情况。由于二位数组不能开过于大,所以这也是邻接矩阵存图的限制吧!
djk求单源最短路,邻接矩阵存图
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 5005;
const int INF = 0x3f3f3f3f;
int n, m;
int mmp[MAXN][MAXN];//邻接矩阵存图
bool vis[MAXN];//对点进行标记
int dis[MAXN];//从起点到各个点的最短距离
void init(int n)//对图进行初始化
{
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(i==j) mmp[i][j] = 0;
else mmp[i][j] = mmp[j][i] = INF;
}
}
}
void getmap(int m)//建图
{
int u, v, val;//u->v,边权为val
for(int i=1; i<=m; i++)
{
scanf("%d %d %d",&u,&v,&val);
u++; v++;//可针对不同的题目进行修改,本人比较喜欢从1开始编号
mmp[u][v] = mmp[v][u] = min(mmp[u][v],val);//无向图(取最小值的原因是若u->v存在多条路,应保存最小的那一条)
// mmp[u][v] = min(mmp[u][v],val);//有向图
}
}
void djk(int st, int ed)//起点为st,终点为ed;
{
for(int i=1; i<=n; i++)
{
vis[i] = false;
dis[i] = mmp[st][i];
}
vis[st] = true;//将起点放入vis数组中
for(int i=1; i<=n-1; i++)//对除了起点之外的边进行遍历,即一共遍历嗯n-1次,为的是将它依次放入vis数组中
{
int mn = INF, id = -1;
for(int j=1; j<=n; j++)
{
if(!vis[j] && dis[j]<mn)
{
id = j;//记录下该点
mn = dis[j];
}
}
if(id==-1) break;//若找不到,可以直接退出循环
vis[id] = true;//将新取出的点放入vis
for(int j=1; j<=n; j++)
{
if(!vis[j] && mmp[id][j] != INF)//对没被放入vis数组的点和存在id->j这条边的点进行更新
{
if(dis[j] > mn+mmp[id][j]) //脑海中浮现一个三角形
{
dis[j] = mn+mmp[id][j];
}
}
}
}
if(dis[ed]!=INF) cout<< dis[ed] <<endl;
else cout << "-1" <<endl;
}
int main()
{
while(~scanf("%d %d",&n,&m))
{
init(n);
getmap(m);
int x, y;
cin>> x >> y;
x++; y++;//切记要看清题目中的城市编号。
djk(x,y);
}
return 0;
}
Two(链式前向星(优先队列优化) 时间复杂度 O(E * log(V)) )
思想和上面是一样的,只不过是存图方式改变了而已。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
#define CLR(a,b) memset(a,b,sizeof(a));
const int N = (int)1e5+10; //最大点数
const int M = (int)1e6+10; //最大边数
const int INF = (int)0x3f3f3f3f;
int n, m; //n是图中点的数目,m是图中边的数目
int head[N], top;
bool vis[N];
int dis[N]; //保存结果
struct Edge //边的定义
{
int to, val, next;
Edge(){}
Edge(int _to, int _val, int _next)
{
to = _to; val = _val; next = _next;
}
}edge[M << 1]; //如果是双向图的的话,边的数量是题目中描述的二倍
void init()
{
CLR(head,-1);
top = 0;
}
void Add(int u, int v, int val) //加单向边
{
edge[top] = Edge(v,val,head[u]);
head[u] = top++;
}
void getmap(int m)
{
int u, v, val;
while(m--)
{
scanf("%d %d %d",&u,&v,&val);
u++; v++;
Add(u, v, val);
Add(v, u, val); //如果是双向图,需要加上这个代码
}
}
void dijkstra(int st, int ed)
{
CLR(vis,false);
CLR(dis,INF); //初始化dis数组
priority_queue<pii, vector<pii>, greater<pii> > qu;///定义pii类型的优先队列
dis[st] = 0;
qu.push(make_pair(0,st)); //make_pair(dis, v) 表示v到起点的距离为dis
while(!qu.empty())
{
pii p = qu.top(); //每次离起点最近的
qu.pop();
int v = p.second;
if(vis[v]) continue; //如果该点已经访问过,则跳过
vis[v] = true;
for(int i=head[v]; ~i; i=edge[i].next)//构建一个三角形。看是否能够通过该点使距离缩短
{
Edge e = edge[i];
if(dis[e.to] > dis[v] + e.val)
{
dis[e.to] = dis[v] + e.val;
qu.push(make_pair(dis[e.to],e.to)); //更新e.to 到 起点的距离
}
}
}
printf("%d\n", dis[ed]==INF ? -1 : dis[ed]);
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
while(~scanf("%d %d",&n,&m))
{
init();
getmap(m);
int x, y;
scanf("%d %d",&x,&y);
x++; y++;
dijkstra(x,y);
}
return 0;
}