dijkstra算法是一个求从源点到图上所有点最短路径的一种算法
注意事项:如果权值为负的是不能用dijkstra算法的,因为假设存在一个回路,回路上的和小于0,则一直走这个回路,最短路径会变成无穷小
过程:1.首先定义一个数组int型dis[maxN],dis[i]代表从源点到i的最短路径,然后全部赋值为inf(无穷大,或者一个很大的数字)
定义一个定义bool数组visit[maxN],visit[i]为false代表没有访问过i,为true代表访问过i
定义一个邻接矩阵map[maxN][maxN],map[i][j]代表i到j的代价为map[i][j],如果为inf则没有路
假设起点为s,令dis[s]=0
2.找到dis数组中最小的且没有访问过的点pos,令visit[pos]=true,代表pos已经被访问过了
3.松弛,遍历数组dis,对于没有访问过的点i,dis[i]=min(dis[i],dis[pos]+map[pos][i]),相当于从源点到i,是直接过去短,还是经过pos点在过去短(对于访问过的点,是不存在更短的路径的,因为每次都是取最短的,比如依次访问i,j,每次都是取最短,则dis[i]<dis[j],min(dis[i],dis[j]+map[i][j])=dis[i],所以不存在更短的)
4.重复2,3,直到所有的点都被访问过了
注意事项:如果权值为负的是不能用dijkstra算法的,因为假设存在一个回路,回路上的和小于0,则一直走这个回路,最短路径会变成无穷小
#include<iostream>
#include<cstring>
#define min(a,b) (a<b?a:b)//两者较小的
const int maxN = 10000, inf = 0x3f3f3f3f;
bool visit[maxN];
int dis[maxN], map[maxN][maxN], n;
void dijkstra(const int &s,const int &t) {
//visit[i]为i是否被访问过,一开始都没访问过,所以都为false
memset(visit, false, sizeof(visit));
//dis[i]为源点s到i的最短路径,一开始都为inf
memset(dis, inf, sizeof(dis));
//起点到起点显然路径为0
dis[s] = 0;
int pos;
for (int i = 1; i <= n; ++i) {
pos = -1;
//需找最小的dis,pos赋值为-1是为了找到第一个
for (int j = 1; j <= n; ++j)
if (!visit[j] && (pos == -1 || dis[pos] < dis[j]))pos = j;
//如果是-1说明都访问过了
if (pos == -1)break;
//如果终点是t,就说明已经到终点了,如果需要遍历图的话,下面就不用写了
if (pos == t)break;
//松弛
for (int j = 1; j <= n; ++j)
if (!visit[j])dis[j] = min(dis[j], dis[pos] + map[pos][j]);
}
//如果dis[t]=inf说明没有路到达t,否则最短路径为dis[t]
if (dis[t] == inf)printf("-1");
else printf("%d\n", dis[t]);
}
int main() {
int m, a, b, c;
scanf("%d%d", &n, &m);
//map为邻接矩阵,先假设都没有路,即为inf
memset(map, inf, sizeof(map));
while (m--) {
//输入a,b,c,代表a到b有一条代价为c的路
scanf("%d%d%d", &a, &b, &c);
//因为是双向边,所以map[a][b]=map[b][a],但是如果题目没说双向边,就直接map[a][b]
//min(map[a][b],c)是为了防止有重边,有重边时显然应该选择代价小的,但是如果题目说没有重边可以直接赋值c
map[a][b] = map[b][a] = min(map[a][b], c);
}
dijkstra();
}
路径还原
#include<iostream>
#include<cstring>
#include<stack>
#define min(a,b) (a<b?a:b)
using namespace std;
const int maxN = 100, inf = 0x3f3f3f3f;
bool visit[maxN];
int path[maxN], dis[maxN], map[maxN][maxN], n;
void dijkstra() {
memset(visit, false, sizeof(visit));
memset(dis, inf, sizeof(dis));
dis[1] = 0;
int pos;
for (int i = 1; i <= n; ++i) {
pos = -1;
for (int j = 1; j <= n; ++j)
if (!visit[j] && (pos == -1 || dis[j] < dis[pos]))pos = j;
if (pos == -1)break;
visit[pos] = true;
for (int j = 1; j <= n; ++j)
if (!visit[j] && dis[j] > dis[pos] + map[pos][j]) {
dis[j] = dis[pos] + map[pos][j];
path[j] = pos;
}
}
}
void getPath() {
stack<int> s;
int t = n;
for (; t != -1; t = path[t])s.push(t);
while (!s.empty()) {
printf("%d ", s.top());
if (s.top() != n)printf("->");
s.pop();
}
printf("\n");
}
int main() {
int m, a, b, c;
scanf("%d%d", &n, &m);
memset(map, inf, sizeof(map));
while (m--) {
scanf("%d%d%d", &a, &b, &c);
map[a][b] = map[b][a] = min(map[a][b], c);
}
memset(path, -1, sizeof(path));
dijkstra();
printf("最短路径距离:%d\n", dis[n]);
printf("最短路径:");
getPath();
return 0;
}
显然算法时间时间复杂度o(n^2)
当然,可以进行优先队列优化,队列优化的可以不用管重边
具体操作就是用邻接表储存,然后定义一个优先队列,把起点的信息入队,然后每次队列取代价最小的点,由这个点遍历所有可以访问的点然后循环
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxN = 10001, inf = 0x3f3f3f3f;
vector<pair<int, int> > edge[maxN];//邻接表,pair第一个代表点,第二个代表代价,注意千万别写pair<int,int>>,最后两>一定要有一个空格,不然就认为是位运算右移了
int n, dis[maxN];
void init() {
for (int i = 1; i <= n; ++i) {
edge[i].clear();//清空邻接表
dis[i] = inf;
}
}
void dijkstra(const int &s, const int &t) {
dis[s] = 0;
priority_queue<pair<int, int> > q;//优先队列,pair第一个是代价,第二个是点
//优先队列默认是从大到小的,pair的比较规则是先比第一个,再比第二个,所以dis[s]加一个负号就相当于从小到大了,由于优先队列只是为了取出点,所以第一个元素是什么都无所谓
//制造一个pair用的是make_pair
q.push(make_pair(-dis[s], s));
while (!q.empty()) {
int now = q.top().second;
//找到终点了就终止循环,如果要遍历就不用写这句了
if (now == t)break;
q.pop();
for (int i = 0; i < edge[now].size(); ++i) {
int v = edge[now][i].first;//取出到达的点
//松弛
if (dis[v] > dis[now] + edge[now][i].second) {
dis[v] = dis[now] + edge[now][i].second;
q.push(make_pair(-dis[v], v));//入队
}
}
}
//如果dis[t]=inf说明没有路到达t,否则最短路径为dis[t]
if (dis[t] == inf)printf("-1");
else printf("%d\n", dis[t]);
}
int main() {
int m, a, b, c;
init();
while (m--) {
scanf("%d%d%d", &a, &b, &c);
//邻接表,代表a到b有一条价值为c的边
edge[a].push_back(make_pair(b, c));
//双向边,如果不是下面这句就不用写了
edge[b].push_back(make_pair(a, c));
}
dijkstra();
return 0;
}
类的版本||结构体的版本,其实差不多
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxN = 100001, inf = 0x3f3f3f3f;
class point {
public:
//v是到达的点,p是代价
int v, p;
point(const int &v, const int &p) :v(v), p(p) {}
//优先队列一定要重载小于号
bool operator <(const point &t)const {
return p > t.p;
}
};
vector<point> edge[maxN];
int dis[maxN], n;
void init() {
for (int i = 1; i <= n; ++i) {
edge[i].clear();
dis[i] = inf;
}
}
void dijkstra(const int &s,const int &t) {
priority_queue<point> q;
dis[s] = 0;
q.push(point(s, dis[s]));
while (!q.empty()) {
int now = q.top().v;
if (now == t)break;
q.pop();
for (int i = 0; i < edge[now].size(); ++i) {
int v = edge[now][i].v;
if (dis[v] > dis[now] + edge[now][i].p) {
dis[v] = dis[now] + edge[now][i].p;
q.push(point(v, dis[v]));
}
}
}
//如果dis[t]=inf说明没有路到达t,否则最短路径为dis[t]
if (dis[t] == inf)printf("-1");
else printf("%d\n", dis[t]);
}
int main() {
int m, a, b, c;
init();
while (m--) {
scanf("%d%d%d", &a, &b, &c);
edge[a].push_back(point(b, c));
//双向边
edge[b].push_back(point(a, c));
}
dijkstra();
return 0;
}
路径还原
#include<iostream>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
const int maxN = 1000, inf = 0x3f3f3f3f;
vector<pair<int, int> > edge[maxN];
int n, dis[maxN], path[maxN];
void init() {
for (int i = 1; i <= n; ++i) {
edge[i].clear();
dis[i] = inf;
path[i] = -1;
}
}
void dijkstra() {
priority_queue<pair<int, int> > q;
q.push(make_pair(0, 1));
dis[1] = 0;
while (!q.empty()) {
int now = q.top().second;
if (now == n)return;
q.pop();
for (int i = 0; i < edge[now].size(); ++i) {
int v = edge[now][i].first;
if (dis[v] > dis[now] + edge[now][i].second) {
dis[v] = dis[now] + edge[now][i].second;
q.push(make_pair(-dis[v], v));
path[v] = now;
}
}
}
}
void getPath() {
stack<int> s;
int t = n;
for (; t != -1; t = path[t])s.push(t);
while (!s.empty()) {
printf("%d ", s.top());
if (s.top() != n)printf("->");
s.pop();
}
printf("\n");
}
int main() {
int m, a, b, c;
scanf("%d%d", &n, &m);
init();
while (m--) {
scanf("%d%d%d", &a, &b, &c);
edge[a].push_back(make_pair(b, c));
edge[b].push_back(make_pair(a, c));
}
dijkstra();
printf("最短路径距离:%d\n", dis[n]);
printf("最短路径:");
getPath();
return 0;
}
算法复杂度o((m+n)*log(n))
—————————————————————————-
题目
poj1502
题目大意:告诉你图的下三角,求从源点到达所有点的最短路径的最大值
解法:实际上就是裸的dijkstra
#include<iostream>
#include<cstring>
#include<stdlib.h>
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
const int maxN = 101, inf = 0x3f3f3f3f;
bool visit[maxN];
int map[maxN][maxN], dis[maxN], n;
void dijkstra() {
memset(visit, false, sizeof(visit));
memset(dis, inf, sizeof(dis));
dis[1] = 0;
int pos;
for (int i = 1; i <= n; ++i) {
pos = -1;
for (int j = 1; j <= n; ++j)
if (!visit[j] && (pos == -1 || dis[j]<dis[pos]))pos = j;
if (pos == -1)return;
visit[pos] = true;
for (int j = 1; j <= n; ++j)
if (!visit[j])dis[j] = min(dis[j], dis[pos] + map[pos][j]);
}
}
int main() {
char s[100];
int ans;
while (scanf("%d", &n) != EOF) {
memset(map, inf, sizeof(map));
ans = map[1][1] = 0;
for (int i = 2; i <= n; ++i) {
map[i][i] = 0;
for (int j = 1; j<i; ++j) {
scanf("%s", s);
if (s[0] == 'x')continue;
map[i][j] = map[j][i] = atoi(s);
}
}
dijkstra();
for (int i = 1; i <= n; ++i)ans = max(dis[i], ans);
printf("%d\n", ans);
}
return 0;
}
———————————————————-
poj3268
题目大意:n个农场,选一个农场x嗨,问每个农场到x再回去,求所有最短路径的最大值
解法:单向边,从i到x走一次,x到i走一次,加起来就是一个最短路径了,然后取最大值
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#define max(a,b) (a>b?a:b)
using namespace std;
const int maxN=1001,inf=0x3f3f3f3f;
vector<pair<int,int> > edge[maxN];
int dis[maxN];
int dijkstra(const int &s,const int &e){
memset(dis,inf,sizeof(dis));
priority_queue<pair<int,int> > q;
dis[s]=0;
q.push(make_pair(dis[s],s));
while(!q.empty()){
int now=q.top().second;
if(now==e)return dis[e];
q.pop();
for(int i=0;i<edge[now].size();++i){
int v=edge[now][i].first;
if(dis[v]>dis[now]+edge[now][i].second){
dis[v]=dis[now]+edge[now][i].second;
q.push(make_pair(-dis[v],v));
}
}
}
}
int main(){
int n,m,x,a,b,c,ans;
while(scanf("%d%d%d",&n,&m,&x)!=EOF){
for(int i=1;i<n;++i)edge[i].clear();
while(m--){
scanf("%d%d%d",&a,&b,&c);
edge[a].push_back(make_pair(b,c));
}
ans=0;
for(int i=1;i<=n;++i)ans=max(ans,dijkstra(i,x)+dijkstra(x,i));
printf("%d\n",ans);
}
return 0;
}
——————————
poj2387
题目大意:求从n到1的最短路径
解法:裸的dijkstra,两种都贴一下吧
#include<iostream>
#include<cstring>
#define min(a,b) (a<b?a:b)
const int maxN=1001,inf=0x3f3f3f3f;
bool visit[maxN];
int dis[maxN],map[maxN][maxN],n;
void dijkstra(){
memset(visit,false,sizeof(visit));
memset(dis,inf,sizeof(dis));
dis[n]=0;
int pos;
for(int i=1;i<=n;++i){
pos=-1;
for(int j=1;j<=n;++j)
if(!visit[j]&&(pos==-1||dis[j]<dis[pos]))pos=j;
visit[pos]=true;
for(int j=1;j<=n;++j)
if(!visit[j])dis[j]=min(dis[j],dis[pos]+map[pos][j]);
}
printf("%d\n",dis[1]);
}
int main(){
int t,a,b,c;
while(scanf("%d%d",&t,&n)!=EOF){
memset(map,inf,sizeof(map));
while(t--){
scanf("%d%d%d",&a,&b,&c);
map[a][b]=map[b][a]=min(map[a][b],c);
}
dijkstra();
}
return 0;
}
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxN=1001,inf=0x3f3f3f3f;
vector<pair<int,int> > edge[maxN];
int dis[maxN],n;
void init(){
for(int i=1;i<=n;++i){
edge[i].clear();
dis[i]=inf;
}
}
void dijkstra(){
dis[n]=0;
priority_queue<pair<int,int> > q;
q.push(make_pair(-dis[n],n));
while(!q.empty()){
int now=q.top().second;
q.pop();
for(int i=0;i<edge[now].size();++i){
int v=edge[now][i].first;
if(dis[v]>dis[now]+edge[now][i].second){
dis[v]=dis[now]+edge[now][i].second;
q.push(make_pair(-dis[v],v));
}
}
}
printf("%d\n",dis[1]);
}
int main(){
int t,a,b,c;
while(scanf("%d%d",&t,&n)!=EOF){
init();
while(t--){
scanf("%d%d%d",&a,&b,&c);
edge[a].push_back(make_pair(b,c));
edge[b].push_back(make_pair(a,c));
}
dijkstra();
}
return 0;
}
———————-
poj1062
题目大意:每个人有个宝贝,交换宝贝要一定的钱,但是可以用指定的其他宝贝抵一部分,每个人有等级,等级差超过了一定的范围是会拒绝交易的,也不能间接交易,即和a交易完和b交易,a和b的等级差也不能超过范围,第一个人有个妹子,问取的妹子最少要多少钱
解法:题目并没有起点,那么我们的dis数组就可以改一改,原本的意义是从源点到终点的最短路径,我们改成其他点在不用宝贝抵钱的情况下需要的钱,至于等级差,因为都要访问1,所以以1的等级为标准,遍历1的等级的左邻域,即[level(1)-m,m],对于任意的u属于那个领域,每次最短路径时只访问[u,u+m]的,最后输出最短的
#include<iostream>
#include<cstring>
#define min(a,b) (a<b?a:b)
const int maxN = 101, inf = 0x3f3f3f3f;
bool visit[maxN];
int map[maxN][maxN], dis[maxN], m, n, p[maxN], l[maxN];
int dijkstra(int level) {
for (int i = 1; i <= n; ++i)dis[i] = p[i];
memset(visit, false, sizeof(visit));
for (int i = 1; i <= n; ++i)
if (l[i] - level > m || l[i] < level)visit[i] = true;
int pos;
for (int i = 1; i <= n; ++i) {
pos = -1;
for (int j = 1; j <= n; ++j)
if (!visit[j] && (pos == -1 || dis[j] < dis[pos]))pos = j;
if (pos == 1)return dis[1];
if (pos == -1)break;
visit[pos] = 1;
for (int j = 1; j <= n; ++j)
if (!visit[j])dis[j] = min(dis[j], dis[pos] + map[pos][j]);
}
return dis[1];
}
int main() {
int x, t, v, ans;
while (scanf("%d%d", &m, &n) != EOF) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)map[i][j] = inf;
for (int i = 1; i <= n; ++i) {
scanf("%d%d%d", &p[i], &l[i], &x);
while (x--) {
scanf("%d%d", &t, &v);
map[t][i] = min(map[t][i], v);
}
}
ans = inf;
for (int i = l[1] - m > 0 ? l[1] - m : 0; i <= l[1]; ++i)
ans = min(ans, dijkstra(i));
printf("%d\n", ans);
}
return 0;
}
———————————–
poj1724
题目大意:求从1到n的最短距离且花费不超过k
解法:dis[maxN]改成dis[maxN][10005]因为多了一个条件,就要多存一个维度,dis[i][j]代表源点到i要花j元,剩下的就和dijkstra差不多了,只要钱超过了就不要这种状态,据说邻接矩阵会超时,用邻接表的
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxN = 101, inf = 0x3f3f3f3f;
class point {
public:
int v, l, p;
point(int v, int l, int p) :v(v), l(l), p(p) {}
bool operator <(const point &t)const {
return l>t.l;
}
};
vector<point> edge[maxN];
int n, k, dis[maxN][10001];
void init() {
for (int i = 1; i <= n; ++i) {
edge[i].clear();
for (int j = 0; j <= k; ++j)dis[i][j] = inf;
}
}
void dijkstra() {
dis[1][0] = 0;
priority_queue<point> q;
q.push(point(1, 0, 0));
while (!q.empty()) {
point t = q.top();
if (t.v == n) {
printf("%d\n", t.l);
return;
}
q.pop();
int u = t.v, l = t.l, p = t.p, price;
for (int i = 0; i<edge[u].size(); ++i) {
price = p + edge[u][i].p;
int v = edge[u][i].v;
if (price>k)continue;
if (dis[v][price] > dis[u][p] + edge[u][i].l) {
dis[v][price] = dis[u][p] + edge[u][i].l;
q.push(point(v, dis[v][price], price));
}
}
}
printf("-1\n");
}
int main() {
int r, s, d, l, t;
while (scanf("%d%d%d", &k, &n, &r) != EOF) {
init();
while (r--) {
scanf("%d%d%d%d", &s, &d, &l, &t);
edge[s].push_back(point(d, l, t));
}
dijkstra();
}
return 0;
}
————————
poj1797
题目大意:每条边都有载重能力,求1到n的最大的载重
解法:dijkstra的变形,dis的意义改成源点到终点的最大的载重,并初始化为0,然后改一下松弛条件,从源点到i的最大载重和min(源点到now的载重,now到i的载重)取大的
#include<iostream>
#include<vector>
#include<queue>
#define min(a,b) (a<b?a:b)
using namespace std;
const int maxN = 1001;
vector<pair<int, int> > edge[maxN];
int dis[maxN], n;
void init() {
for (int i = 1; i <= n; ++i) {
edge[i].clear();
dis[i] = 0;
}
}
void dijkstra() {
priority_queue<pair<int, int> > q;
q.push(make_pair(1000000, 1));
dis[1] = 1000000;
while (!q.empty()) {
int now = q.top().second;
q.pop();
for (int i = 0; i<edge[now].size(); ++i) {
int v = edge[now][i].first;
if (dis[v]<min(dis[now], edge[now][i].second)) {
dis[v] = min(dis[now], edge[now][i].second);
q.push(make_pair(dis[v], v));
}
}
}
printf("%d\n\n", dis[n]);
}
int main() {
int t, m, a, b, c;
scanf("%d", &t);
for (int i = 1; i <= t; ++i) {
scanf("%d%d", &n, &m);
init();
while (m--) {
scanf("%d%d%d", &a, &b, &c);
edge[a].push_back(make_pair(b, c));
edge[b].push_back(make_pair(a, c));
}
printf("Scenario #%d:\n", i);
dijkstra();
}
return 0;
}
——————————–
poj2253
题目大意:从1跳到2,求到达2的最小跳跃距离
解法:题目给的坐标,由坐标转化为距离,然后改变松弛条件,源点到i的最小跳跃距离和max(源点到now的最小跳跃距离,now到i的跳跃距离),取小的,然后依然dijkstra
#include<iostream>
#include<cmath>
#include<vector>
#include<queue>
#define max(a,b) (a>b?a:b)
using namespace std;
const int maxN=201,inf=0x3f3f3f3f;
vector<pair<int,double> > edge[maxN];
double dis[maxN];
int n;
double getDistance(const int &x1,const int &y1,const int &x2,const int &y2){
return sqrt(1.0*(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
void init(){
for(int i=1;i<=n;++i){
edge[i].clear();
dis[i]=inf;
}
}
void dijkstra(){
priority_queue<pair<double,int> > q;
dis[1]=0;
q.push(make_pair(0,1));
while(!q.empty()){
int now=q.top().second;
q.pop();
for(int i=0;i<edge[now].size();++i){
int v=edge[now][i].first;
if(dis[v]>max(dis[now],edge[now][i].second)){
dis[v]=max(dis[now],edge[now][i].second);
q.push(make_pair(dis[v],v));
}
}
}
printf("Frog Distance = %.3f\n\n",dis[2]);
}
int main(){
int path[maxN][2],cnt=0;
while(scanf("%d",&n)&&n){
++cnt;
init();
for(int i=1;i<=n;++i)scanf("%d%d",&path[i][0],&path[i][1]);
printf("Scenario #%d\n",cnt);
for(int i=1;i<n;++i){
for(int j=i+1;j<=n;++j){
edge[i].push_back(make_pair(j,getDistance(path[i][0],path[i][1],path[j][0],path[j][1])));
edge[j].push_back(make_pair(i,getDistance(path[i][0],path[i][1],path[j][0],path[j][1])));
}
}
dijkstra();
}
return 0;
}
———————-