题目描述
最短路的疑惑
时间限制:1
秒 内存限制:128
M
题目描述
~~小可在做最短路的时候,碰见了一位大佬,于是赶紧去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
,表示x
到y
有条边,权值为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嫖”