- 单源
- 权不为负数
一个讲的挺好的b站视频 https://www.bilibili.com/video/BV1zz4y1m7Nq?from=search&seid=8790821936431970385&spm_id_from=333.337.0.0

Dijkstra算法邻接矩阵代码模板 O(n2)
适合图的点数不超过1000的情况
//全局变量部分const int MAXN = 1000; //图的最大顶点数const int INF = 1000000000; //设立INF为一个极大数,//这里这个数也可以设置为0x3f3f3f3f,这代表无穷大,即1061109567这一数值int n; //当前图的点数int vis[MAXN]; //标记数组用于记录图中各个点的被访问情况,0为未访问,1为已访问int dis[MAXN]; //记录起点到各个顶点的最短路径长度int m[MAXN][MAXN]; //用一个邻接矩阵来记录图中各个点的连接情况void Dijkstra(int s){ //s为起点fill(vis, vis+MAXN, 0); //将标记数组初始化为0即未被访问状态,此处可用memsetfill(dis, dis+MAXN, INF); //将最短路数组初始化为一个很大的数,此处要注意不可用memsetdis[s]=0; //首先将起点s到达自身的最短路设为0for(int i=0;i<n;i++){ //n次循环,遍历完n个点int node = -1; //node记录当前能找到的从起点到此没被访问的最短的一个点int minn = INF; //minn记录到达那个点的最短路径for(int j=0;j<n;j++){ //每个点逐步寻找没被访问的最短的一个点if(vis[j]==0 && dis[j]<minn){minn = dis[j];node = j;}}if(node==-1){ //找不到小于INF的一个点,说明其他结点与定点不连通return ;}vis[node] = 1; //将当前点标记为1for(int j=0;j<n;j++){if(vis[j]==0 && m[node][j]!=INF && (dis[node]+m[node][j])<dis[j]){//如果结点未被访问且 node能到达此结点 并且从起点到此结点过node中介点更近//在此标尺只有距离,如果有两条距离相等或多条相等,那么就只需在此进行修改//如路径相同第二标尺为花费时,则可通过在增加花费数组,在此if后加elseif判断距离相等情况花费不同条件。//下题就有两标尺。dis[j] = dis[node]+m[node][j];}}}}
二维矩阵+堆优化:个人认为最好写最实用
总结一下,Dijkstra算法的流程就是,不断取出离顶点最近而没有被访问过的点,松弛它和它能到达的所有点。
如何取出离顶点最近的点?如果暴力寻找,那就是朴素的Dijkstra算法,时间复杂度是O^2
但我们可以采取堆优化。具体而言,我们可以用一个优先队列(或手写堆,那样更快)来维护所有节点。这样可以实现 mlogm 的算法
int dis[maxn], vis[maxn] = {0};vector<pair<int, int>> E[maxn];//存图 <u, d> E[i]到u点距离为dtypeof pair<int ,int> PAIR; //<起点到x点的距离, x>#define MP make_pairpriority_queue<PAIR, vector<PAIR>, greater<PAIR>> Q;//距离短的优先级高,也就是 Q.top()void Dij(int s){fill(dis, dis + maxn, INT_MAX);fill(vis, vis + maxn, 0);dis[s] = 0;Q.push(MP(0,s));while(!Q.empty()){int u = Q.top().second;Q.pop();if(vis[u])continue;vis[u] = 1;for(auto it : E[u]){if(dis[it.first] > it.second + dis[u]){dis[it.first] = it.second + dis[u];//更新dis//或许还有其他的操作}else if(){}//有时还有其他的判断条件if(!vis[it.first]) Q.push(MP(dis[it.first], it.first));}}}
优化:链式前向星+堆优化 O(mlogm)
链式前向星
用一个优先队列(或手写堆,那样更快)来维护所有节点。
typedef pair<int, int> Pair; //<x点到起点的距离, x>priority_queue<Pair, vector<Pair>, greater<Pair> > Q; // // 这里是greater, 使得距离短的先出队void Dij(int s) //传入起点编号{fill(vis, vis+MAXN, 0);fill(dis, dis+MAXN, INF);dist[s] = 0;Q.push(make_pair(0, s));while (!Q.empty()){int p = Q.top().second;Q.pop();if (vis[p])continue;vis[p] = 1;for (int e = head[p]; e != 0; e = edges[e].next){int to = edges[e].to;dist[to] = min(dist[to], dist[p] + edges[e].w);if (!vis[to])Q.push(make_pair(dist[to], to));}}}
题目
L2-001 紧急救援 (25 分)
#include <bits/stdc++.h>using namespace std;const int INF = 1e8;//N<500int saveTeam[505];//各点的存储的救援人数int st[505];//按路径能召集到的最多人数int mm[505][505];//地图int number[505];//到该点最短路径的数量int vis[505];//是否标记为node过int dis[505];//起点到该点的距离int father[505];//最短路径中,该点的上一个节点int n,m,s,d;//城市数量、路线数量、起点、终点void Dijkstra(){//初始化fill(dis,dis+n,INF);fill(vis,vis+n,0);fill(number,number+5,0);dis[s]=0;st[s]=saveTeam[s];number[s]=1;for(int i=0; i<n;i++){int minn = INF,node = -1;for(int j =0;j<n;j++){if(!vis[j] && dis[j]<minn){minn = dis[j];node = j;}}vis[node]=1;if(node == -1)return;for(int j =0;j<n;j++){if(vis[j]==0 && mm[node][j]!=-1 && (dis[node]+mm[node][j]) <dis[j]){dis[j]=dis[node]+mm[node][j];st[j] = st[node]+saveTeam[j];father[j]=node;number[j] = number[node];}else if(vis[j]==0 && mm[node][j]!=-1 && (dis[node]+mm[node][j])==dis[j]){if((st[node]+saveTeam[j]) > st[j]){st[j] = st[node]+saveTeam[j];father[j]=node;}number[j]+=number[node];}}}}int main(){vector<int> pathV;cin>>n>>m>>s>>d;for(int i = 0;i<n;i++){cin>>saveTeam[i];}//初始化地图for(int i = 0;i<n;i++){for(int j = 0;j<n;j++){mm[i][j] = -1;}}//读入地图for(int i =0;i<m;i++){int n1,n2,val;cin>>n1>>n2>>val;mm[n1][n2]=val;mm[n2][n1]=val;}Dijkstra();int p =d;while(1){if(p==father[p]){//应该是0,即没有father// cout<<"pfather"<<p<<" "<<father[p]<<"\n";pathV.push_back(p);break;}pathV.push_back(p);// cout<<"pfather"<<p<<" "<<father[p]<<"\n";p = father[p];}cout<<number[d]<<" "<<st[d]<<endl;// cout<<pathV.size();for(int i = pathV.size()-1; i>=0;i--){if(i==pathV.size()-1){cout<<pathV[i];}else{cout<<" "<<pathV[i];}}}
L3-005 垃圾箱分布 (30 分)
题目大意:
- 从m个垃圾站里面选取1个站点,让他离居民区的最近的人最远,并且没有超出服务范围ds之内。
- 如果有很多个最远的垃圾站,输出距离所有居民区距离平均值最小的那个。
- 如果平均值还是一样,就输出按照顺序排列垃圾站编号最小的那个
思路:
- 垃圾站之间也是彼此有路连接的,最短路径计算也要把垃圾站算上。
- 所以我们就是对 n+m 个点进行Dijkstra计算最短路径,计算出1~m号垃圾站距离其他站点的最短路径。
- 遍历dis数组,如果dis存在一个距离大于服务范围ds的距离,那么我们就舍弃这个垃圾站。
- 取最短的路径,这就是距离它最近的垃圾站mindis。如果mindis > ansdis,更新。
G开头的,编号设为n+G后面的数字
#include <cstdio>#include <algorithm>#include <iostream>#include <string>using namespace std;const int inf = 999999999;int n, m, k, ds, station;int e[1020][1020], dis[1020];bool visit[1020];int main() {//初始化地图,没路的距离就是inffill(e[0], e[0] + 1020 * 1020, inf);fill(dis, dis + 1020, inf);scanf("%d%d%d%d", &n, &m, &k, &ds);for(int i = 0; i < k; i++) {int tempdis;string s, t;cin >> s >> t >> tempdis;int a, b;//注意处理字符串//这样处理最后一个点会错误——没注意是小于“等于”10情况...// if(s[0] == 'G') {// a = s[1] - '0' + n;// } else {// a = stoi(s);// }// if(t[0] == 'G') {// b = t[1] - '0' + n;// } else {// b = stoi(t);// }if(s[0] == 'G') {s = s.substr(1);a = n + stoi(s);} else {a = stoi(s);}if(t[0] == 'G') {t = t.substr(1);b = n + stoi(t);} else {b = stoi(t);}//将数据添入地图e[a][b] = tempdis;e[b][a] = tempdis;}int ansid = -1;//存储最终选择的候选编号,如果没有符合要求的 就是-1double ansdis = -1, //最终的最短路径ansaver = inf;//最终的平均路径长度//对每个候选点进行dij算法:将其作为起点,算出到各个居民的距离——存储在dis中for(int index = n + 1; index <= n + m; index++) {double mindis = inf, //候选点到各个居民的路径中最短的长度aver = 0; //该候选点到各点的平均长度fill(dis, dis + 1020, inf); //重置disfill(visit, visit + 1020, false); //重置visdis[index] = 0; //起点到自身距离自然为0for(int i = 0; i < n + m; i++) {int u = -1, //本轮距离起点最近的点minn = inf;//最近距离for(int j = 1; j <= n + m; j++) {if(visit[j] == false && dis[j] < minn) {u = j;minn = dis[j];}}if(u == -1) break;//没其他点能到起点了visit[u] = true;for(int v = 1; v <= n + m; v++) {if(visit[v] == false && dis[v] > dis[u] + e[u][v])dis[v] = dis[u] + e[u][v];}}for(int i = 1; i <= n; i++) {if(dis[i] > ds) { //有距离过远,不符合题意mindis = -1;break;}if(dis[i] < mindis) mindis = dis[i]; //最短路径aver += 1.0 * dis[i];}if(mindis == -1) continue;aver = aver / n;//判断一下if(mindis > ansdis) {ansid = index;ansdis = mindis;ansaver = aver;} else if(mindis == ansdis && aver < ansaver) {ansid = index;ansaver = aver;}}if(ansid == -1)printf("No Solution");else {printf("G%d\n", ansid - n);printf("%.1f %.1f", ansdis, ansaver);//输出一位小数}return 0;}
L3-011 直捣黄龙
三个条件:最快>城镇最多>有效杀伤最多
- 存储:最合适路径、最快路数、最短进攻距离、歼敌总数
- 数据量:城镇数∈[2, 200]
- 将字符串与数字做映射,方便操作
- Dijskra
#include <bits/stdc++.h>using namespace std;#define ll long long#define FOR(i, n, m) for (int i = n; i < m; ++i)struct node{int id, dis;//方便后面的优先队列bool friend operator<(const node &a, const node &b){return a.dis > b.dis;}};int n, k, s, t, cnt, d,enemyNum[205], //各个大本营敌军数量enemyAll[205], //到该点的总共的歼敌数量pre[205], //到该大本营的上一个点Dis[205], //起点到各点的最短距离vis[205],sum[205], //到该点路线数量liberation[205]; //到该店解放的大本营数//映射map<string, int> ntoi; //name->idmap<int, string> iton; //id->namevector<pair<int, int>> E[205]; //存图, <u,d>:E[i]到u点,权为dvector<int> ans;string S, T, u, v;void Dijskra(){fill(Dis, Dis + 205, 1e9);Dis[s] = 0;sum[s] = 1;priority_queue<node> Q;Q.push(node{s, 0});while (!Q.empty()){int now = Q.top().id, nowD = Q.top().dis;Q.pop();if (vis[now])continue;vis[now] = 1;for (auto it : E[now]){int itV = it.first, itD = it.second;if (Dis[itV] > nowD + itD){Dis[itV] = nowD + itD;enemyAll[itV] = enemyAll[now] + enemyNum[itV];sum[itV] = sum[now];liberation[itV] = liberation[now] + 1;Q.push(node{itV, Dis[itV]});pre[itV] = now;}else if (Dis[itV] == nowD + itD){sum[itV] += sum[now];if (liberation[itV] < liberation[now] + 1){liberation[itV] = liberation[now] + 1;enemyAll[itV] = enemyAll[now] + enemyNum[itV];Q.push(node{itV, Dis[itV]});pre[itV] = now;}else if (liberation[itV] == liberation[now] + 1){if (enemyAll[itV] < enemyAll[now] + enemyNum[itV]){enemyAll[itV] = enemyAll[now] + enemyNum[itV];Q.push(node{itV, Dis[itV]});pre[itV] = now;}}}}}}int main(){ios::sync_with_stdio(false);cin >> n >> k >> S >> T;for (int i = 0; i < n - 1; i++){cin >> u >> d;ntoi[u] = ++cnt;iton[cnt] = u;enemyNum[cnt] = d;}//起点的映射ntoi[S] = 0, iton[0] = S;//终点t = ntoi[T];for (int i = 0; i < k; i++){cin >> u >> v >> d;//存边E[ntoi[u]].push_back({ntoi[v], d});E[ntoi[v]].push_back({ntoi[u], d});}Dijskra();d = t;while (d){ans.push_back(d);d = pre[d];}cout << iton[0];for (int i = ans.size() - 1; i >= 0; i--){cout << "->" << iton[ans[i]];}cout << "\n"<< sum[t] << " " << Dis[t] << " " << enemyAll[t] << "\n";return 0;}
