dijkstra——最短路的疑惑题解


题目描述

最短路的疑惑
时间限制:1秒 内存限制:128M

题目描述
~~小可在做最短路的时候,碰见了一位大佬,于是赶紧去orz了一番。~~之后大佬给了他一道最短路的题目,题目是这么说的:给定一张连通的非负权的有向图,求得root出发到达每个节点的最短距离,并将root中的所有可达节点的最短距离中的最大值设为当前图的直径。求得直径之后,将直径上的每个边进行修改,设当前图的直径的值为A,对于该直径上的每一条边e,如果该边e长度是奇数,则该边e加上A,如果该边e是偶数则该边变为A,对于生成的新图,请你求出新图下root的直径。

输入描述
第一行为两个数n,m,root (1<=n<=1000,1<=m<=2000000)表示有n个点,m条边,起点为root。接下来m行,每行三个整数x,y,z,表示xy有条边,权值为z(1<=z<=1000)。

输出描述
输出占一行,输出题目要求的新图的最短路。题目保证图的直径唯一。

样例
输入

4 3 2
2 3 4
3 4 1
4 1 2

输出

22

这道题的意思是:

小可去膜拜大佬,被大佬的题整EMO了我们先求出连通图的直径,直径就是离root最远的点的距离,然后将整条直径经过的路径进行如下操作:(这里用w代指路径长度,d代指直径)

	当 w 为奇数时(即 w % 2 == 1),加上 d(即 w += d)
	否则(即 w % 2 == 0),变为 d(即 w = d)

进行完此类操作后重新求出直径,并输出

解题思路

跑两遍dijkstra即可,第一遍找出直径,更新路径,再找一遍直径即可
路径可以用pre数组去记录每个点的上一个位置,最后再按照pre[直径]找回来,以来确定更新后的路径

题目处理

初始化

定义变量

struct node { int to, nxt, z; } edge[N1];
int head[N2], idx;
// 链式前向星
int dis[N2]; // dis数组记录最短路径
int pre[N2]; // pre数组记录start到一个点的最短路径中这个点的上一个点
bool vis[N2]; // vis记录哪些点已被确定
int n, m, start; // n, m与题目对应,start与root对应
int ans; // 记录直径

清空head[]与建图

	memset(head, -1, sizeof(head));
	// head数组初始化
	WW(m) {
	    int a, b, c;
	    scanf("%d%d%d", &a, &b, &c);
	    add(a, b, c);
	    // 输入与建图
	}

操作

链式前向星的插入

void add(int u, int v, int z) { edge[idx].to = v, edge[idx].z = z, edge[idx].nxt = head[u], head[u] = idx++; } // 链式前向星专属插入

dijkstra找最短路,并记录每条路径

void dijkstra() {// dijkstra求最短路
	memset(vis, false, sizeof(vis));
	memset(dis, 0x3f, sizeof(dis));
	// 每次进行去找最短路时清空数组
	dis[start] = 0, ans = 0; // 初始化
	repi(2, n) { // 进行n - 1次查找
		int u = -1;
		repj(1, n) if (!vis[j] && (u == -1 || dis[u] > dis[j])) u = j; // 确定新的start到某一点的最短路径
		vis[u] = true; // 更新vis[]
		for(int i = head[u]; ~i; i = edge[i].nxt) // 寻找是否有更短的路线
			if (dis[edge[i].to] > edge[i].z + dis[u]) // 如果找到,更新dis数组与pre数组
				dis[edge[i].to] = edge[i].z + dis[u], pre[edge[i].to] = u;
	}
}

核心代码

寻找初始图的直径,并更新图

	dijkstra(); // 第一遍dijkstra
    int y = start;
    repi(1, n) // 先找到第一轮的直径
    	if (dis[i] > ans) // 如果找到,更新目标点,更新直径
    		ans = dis[i], y = i;
	while(y != start) { // 利用pre顺藤摸瓜找回start,更新路径上每一点
		int yy = pre[y];
		for(int i = head[yy]; ~i; i = edge[i].nxt) {
			if (edge[i].to == y) {
				edge[i].z = edge[i].z % 2 ? edge[i].z + ans : ans;
				break;
			}
		}
		y = yy;
	}

确定答案

    dijkstra(); // 第二遍dijkstra
    repi(1, n) // 找出变化之后的直径,即答案
    	ans = max(ans, dis[i]);

输入输出

输入输出被我吃啦(请自行看AC代码)

Accepted Code

# include <cstdio>
# include <cstring>
# include <cmath>
# include <algorithm>
# include <vector>
# include <iostream>
# include <string>
# include <set>
# include <map>
# include <stack>
# include <queue>
# include <deque>
# define pf push_front
# define pb push_back
# define ppf pop_front()
# define ppb pop_back()
# define mp make_pair
# define p_a first
# define p_b second
# define PI acos(-1, 0)
# define LF putchar('\n')
# define SP putchar(' ')
# define repi(a, b) for(int i = (a); i <= (b); i++)
# define repi_(a, b) for(int i = (a); i >= (b); i--)
# define repj(a, b) for(int j = (a); j <= (b); j++)
# define repj_(a, b) for(int j = (a); j >= (b); j--)
# define repk(a, b) for(int k = (a); k <= (b); k++)
# define repk_(a, b) for(int k = (a); k >= (b); k--)
# define WW(x) while((x)--)
# define paix(a, n) sort((a) + 1, (a) + 1 + (n));
# define BUG(z) cout << endl << (z) << endl;
# define lowbit(z) ((z) & (-z))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int N1 = 2e6 + 5, N2 = 1e3 + 5, M1 = 1e9 + 7, M2 = 998244353, INF1 = 0x7fffffff, INF2 = 0x3f3f3f3f;
const ll INF = 9223372036854775807;
template <typename T>
void read(T& x) { x = 0; char ch = getchar(); ll f = 1; while (!isdigit(ch)) { if (ch == '-') f *= -1; ch = getchar(); } while (isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); } x *= f; }
template <typename T, typename... Args>
void read(T& first, Args&... args) { read(first); read(args...); }
template <typename T>
void write(T arg) { T x = arg; if (x < 0) { putchar('-'); x = -x; } if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
template <typename T, typename... Ts>
void write(T arg, Ts... args) { write(arg); if (sizeof...(args) != 0) { putchar(' '); write(args...); } }
void CLOSE() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); }



// ————————————————————分割线~~~ 
struct node { int to, nxt, z; } edge[N1];
int head[N2], idx;
// 链式前向星
int dis[N2]; // dis数组记录最短路径
int pre[N2]; // pre数组记录start到一个点的最短路径中这个点的上一个点
bool vis[N2]; // vis记录哪些点已被确定
int n, m, start; // n, m与题目对应,start与root对应
int ans; // 记录直径
void add(int u, int v, int z) { edge[idx].to = v, edge[idx].z = z, edge[idx].nxt = head[u], head[u] = idx++; } // 链式前向星专属插入
void dijkstra() {// dijkstra求最短路
	memset(vis, false, sizeof(vis));
	memset(dis, 0x3f, sizeof(dis));
	// 每次进行去找最短路时清空数组
	dis[start] = 0, ans = 0; // 初始化
	repi(2, n) { // 进行n - 1次查找
		int u = -1;
		repj(1, n) if (!vis[j] && (u == -1 || dis[u] > dis[j])) u = j; // 确定新的start到某一点的最短路径
		vis[u] = true; // 更新vis[]
		for(int i = head[u]; ~i; i = edge[i].nxt) // 寻找是否有更短的路线
			if (dis[edge[i].to] > edge[i].z + dis[u]) // 如果找到,更新dis数组与pre数组
				dis[edge[i].to] = edge[i].z + dis[u], pre[edge[i].to] = u;
	}
}
void tain() {
	memset(head, -1, sizeof(head));
	// head数组初始化
	WW(m) {
	    int a, b, c;
	    scanf("%d%d%d", &a, &b, &c);
	    add(a, b, c);
	    // 输入与建图
	}
    dijkstra(); // 第一遍dijkstra
    int y = start;
    repi(1, n) // 先找到第一轮的直径
    	if (dis[i] > ans) // 如果找到,更新目标点,更新直径
    		ans = dis[i], y = i;
	while(y != start) { // 利用pre顺藤摸瓜找回start,更新路径上每一点
		int yy = pre[y];
		for(int i = head[yy]; ~i; i = edge[i].nxt) {
			if (edge[i].to == y) {
				edge[i].z = edge[i].z % 2 ? edge[i].z + ans : ans;
				break;
			}
		}
		y = yy;
	}
    dijkstra(); // 第二遍dijkstra
    repi(1, n) // 找出变化之后的直径,即答案
    	ans = max(ans, dis[i]);
	return ;
}
signed main() {
	/*
    freopen("", "r", stdin);
    freopen("", "w", stdout);
    */
    scanf("%d%d%d", &n, &m, &start);
    tain();
	write(ans); LF;
    /*
    fclose(stdin);
    fclose(stdout);
    */
	return 0;
}

PLEASE “不要BAI嫖”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Dijkstra算法是求单源最短路径的经典算法,其基本思想是通过逐步扩展生成最短路径集合,最终得到源点到所有其它点的最短路径。 以下是C++实现: ```c++ #include <iostream> #include <vector> #include <queue> #include <cstring> using namespace std; const int INF = 0x3f3f3f3f; // 定义正无穷 struct Edge { int to, w; Edge(int to, int w) : to(to), w(w) {} }; vector<Edge> G[100010]; // 邻接表存图 int dist[100010]; // 存储最短路径长度 bool vis[100010]; // 标记是否已经确定最短路径 void dijkstra(int s) { memset(dist, INF, sizeof(dist)); // 初始化距离为正无穷 memset(vis, false, sizeof(vis)); // 初始化标记为未确定最短路径 dist[s] = 0; // 源点到自己的距离为0 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; // 小根堆 q.push(make_pair(0, s)); // 将源点入队 while(!q.empty()) { int u = q.top().second; // 取出当前距离最小的点 q.pop(); if(vis[u]) continue; // 如果已经确定最短路径,直接跳过 vis[u] = true; // 标记为已确定最短路径 for(auto e : G[u]) { // 遍历所有相邻的点 int v = e.to; int w = e.w; if(dist[v] > dist[u] + w) { // 如果当前路径更优 dist[v] = dist[u] + w; // 更新最短路径距离 q.push(make_pair(dist[v], v)); // 将该点加入小根堆 } } } } int main() { int n, m, s; cin >> n >> m >> s; for(int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; G[u].push_back(Edge(v, w)); } dijkstra(s); for(int i = 1; i <= n; i++) { if(dist[i] == INF) cout << "INF" << endl; // 如果不连通,输出INF else cout << dist[i] << endl; } return 0; } ``` 输入格式:第一行输入三个整数n,m,s,表示图的点数、边数和源点编号。接下来m行每行三个整数u,v,w,表示一条从u到v的有向边,边权为w。 输出格式:输出n行,每行一个整数,表示源点到每个点的最短路径长度。若不连通,则输出INF。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值