DIJ最短路 - The Shortest Path in Nya Graph HDU - 4725
题意:
给 定 n 个 点 ( 编 号 从 1 到 n ) , 每 个 点 所 在 的 层 次 为 l i , 相 邻 层 次 之 间 有 一 条 权 值 为 C 的 无 向 边 , 给定n个点(编号从1到n),每个点所在的层次为l_i,相邻层次之间有一条权值为C的无向边, 给定n个点(编号从1到n),每个点所在的层次为li,相邻层次之间有一条权值为C的无向边,
另 有 m 条 额 外 的 无 向 边 ( u , v , w ) 。 另有m条额外的无向边(u,v,w)。 另有m条额外的无向边(u,v,w)。
求 从 1 号 点 到 n 号 点 的 最 短 距 离 。 求从1号点到n号点的最短距离。 求从1号点到n号点的最短距离。
T 组 测 试 数 据 。 T组测试数据。 T组测试数据。
Sample Input
2
3 3 3
1 3 2
1 2 1
2 3 1
1 3 3
3 3 3
1 3 2
1 2 2
2 3 2
1 3 4
Sample Output
Case #1: 2
Case #2: 3
数据范围:
T ≤ 20 , 0 ≤ n , m ≤ 1 0 5 , 1 ≤ C ≤ 1 0 3 , 1 ≤ u , v ≤ n , 1 ≤ w ≤ 1 0 4 , 1 ≤ i ≤ n T\le 20,0\le n,m\le10^5,1\le C\le 10^3,1\le u,v\le n,1\le w\le 10^4,1\le i\le n T≤20,0≤n,m≤105,1≤C≤103,1≤u,v≤n,1≤w≤104,1≤i≤n
分析:
本 题 难 点 在 于 建 图 。 本题难点在于建图。 本题难点在于建图。
如 果 我 们 将 每 一 层 的 结 点 都 存 储 下 来 , 然 后 遍 历 每 一 层 , 将 相 邻 两 层 之 间 的 结 点 两 两 连 接 一 条 边 , 如果我们将每一层的结点都存储下来,然后遍历每一层,将相邻两层之间的结点两两连接一条边, 如果我们将每一层的结点都存储下来,然后遍历每一层,将相邻两层之间的结点两两连接一条边,
若 n 个 结 点 平 均 分 布 在 两 层 , 那 么 这 样 建 图 的 复 杂 度 将 达 到 O ( n 2 ) , 在 本 题 是 不 允 许 的 。 若n个结点平均分布在两层,那么这样建图的复杂度将达到O(n^2),在本题是不允许的。 若n个结点平均分布在两层,那么这样建图的复杂度将达到O(n2),在本题是不允许的。
这 里 采 用 建 立 一 个 虚 拟 ′ ′ 层 结 点 ′ ′ , 来 解 决 这 个 问 题 : 这里采用建立一个虚拟''层结点'',来解决这个问题: 这里采用建立一个虚拟′′层结点′′,来解决这个问题:
相 邻 层 之 间 所 有 的 点 通 过 一 个 ′ ′ 层 结 点 ′ ′ 建 立 连 接 , 相邻层之间所有的点通过一个''层结点''建立连接, 相邻层之间所有的点通过一个′′层结点′′建立连接,
我 们 对 于 l i 层 , 建 立 一 个 虚 拟 的 结 点 V i , 它 相 当 于 l i 层 的 入 口 , 我们对于l_i层,建立一个虚拟的结点V_i,它相当于l_i层的入口, 我们对于li层,建立一个虚拟的结点Vi,它相当于li层的入口,
V i 与 l i 层 内 的 所 有 结 点 建 立 一 条 有 向 边 , 权 值 为 0 , V_i与l_i层内的所有结点建立一条有向边,权值为0, Vi与li层内的所有结点建立一条有向边,权值为0,
相 邻 层 ( l i ± 1 层 ) 的 所 有 结 点 与 V i 建 立 一 条 有 向 边 , 即 通 过 V i 进 入 到 l i 层 , 权 值 设 置 为 C . 相邻层(l_i±1层)的所有结点与V_i建立一条有向边,即通过V_i进入到l_i层,权值设置为C. 相邻层(li±1层)的所有结点与Vi建立一条有向边,即通过Vi进入到li层,权值设置为C.
这 样 , 我 们 就 能 够 建 出 此 图 。 这样,我们就能够建出此图。 这样,我们就能够建出此图。
复杂度分析:
n 个 结 点 分 布 在 k 个 不 同 的 层 次 , 那 么 一 共 就 有 n + k 个 结 点 , n个结点分布在k个不同的层次,那么一共就有n+k个结点, n个结点分布在k个不同的层次,那么一共就有n+k个结点,
① 、 每 个 结 点 需 要 与 层 结 点 建 立 一 条 从 层 结 点 指 出 的 有 向 边 , 共 需 要 建 立 n 条 边 , ①、每个结点需要与层结点建立一条从层结点指出的有向边,共需要建立n条边, ①、每个结点需要与层结点建立一条从层结点指出的有向边,共需要建立n条边,
② 、 每 个 结 点 需 要 与 相 邻 的 两 个 层 结 点 ( 最 上 层 与 最 下 层 只 有 一 个 层 结 点 ) 建 立 一 条 进 入 层 结 点 的 有 向 边 , ②、每个结点需要与相邻的两个层结点(最上层与最下层只有一个层结点)建立一条进入层结点的有向边, ②、每个结点需要与相邻的两个层结点(最上层与最下层只有一个层结点)建立一条进入层结点的有向边,
假 设 每 一 层 的 结 点 数 量 为 c n t i , 共 需 要 建 立 ∑ i = 1 k 2 × c n t i = 2 ∑ i = 1 k c n t i = 2 n 条 边 \qquad 假设每一层的结点数量为cnt_i,共需要建立\sum_{i=1}^k2×cnt_i=2\sum_{i=1}^kcnt_i=2n条边 假设每一层的结点数量为cnti,共需要建立∑i=1k2×cnti=2∑i=1kcnti=2n条边
③ 、 m 条 无 向 边 , 共 需 要 建 立 2 m 条 边 。 ③、m条无向边,共需要建立2m条边。 ③、m条无向边,共需要建立2m条边。
故 复 杂 度 为 O ( 3 n + 2 m ) , 因 此 边 的 数 量 上 限 为 5 × 1 0 5 。 在 这 里 T 了 很 多 发 。 故复杂度为O(3n+2m),因此边的数量上限为5×10^5。在这里T了很多发。 故复杂度为O(3n+2m),因此边的数量上限为5×105。在这里T了很多发。
另外: 多 测 注 意 清 空 邻 接 表 指 针 。 多测注意清空邻接表指针。 多测注意清空邻接表指针。
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#include<vector>
#define x first
#define y second
using namespace std;
const int N = 200010, M = 500010, inf = 0x3f3f3f3f;
typedef pair<int, int> P;
int n, m, C;
int e[M], ne[M], w[M], h[N], idx;
int dis[N];
bool st[N];
vector<int> l[N];
void add(int a, int b, int c)
{
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
int dijkstra()
{
priority_queue<P, vector<P>, greater<P> > heap;
dis[1] = 0;
heap.push(make_pair(0, 1));
while(heap.size())
{
P t = heap.top();
heap.pop();
int u = t.y;
if(st[u]) continue;
st[u] = true;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(dis[j] > dis[u] + w[i])
{
dis[j] = dis[u] + w[i];
heap.push(make_pair(dis[j], j));
}
}
}
return dis[n] == inf ? -1 : dis[n];
}
void cls()
{
idx = 0;
memset(l, 0, sizeof l);
memset(h, -1, sizeof h);
memset(st, 0, sizeof st);
memset(dis, 0x3f, sizeof dis);
}
int main()
{
int T;
scanf("%d", &T);
for(int t = 1; t <= T; t++)
{
cls(); //多测
scanf("%d%d%d", &n, &m, &C);
int layers;
for(int i=1;i<=n;i++)
{
scanf("%d", &layers);
l[layers].push_back(i);
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<l[i].size();j++)
{
add(i+n, l[i][j], 0); //层结点
if(l[i+1].size()) add(l[i][j], i+1+n, C);
if(l[i-1].size()) add(l[i][j], i-1+n, C);
}
}
int a, b, c;
while(m--)
{
scanf("%d%d%d", &a, &b, &c);
add(a, b, c), add(b, a, c);
}
printf("Case #%d: %d\n", t, dijkstra());
}
return 0;
}