DIJ最短路 - The Shortest Path in Nya Graph HDU - 4725

DIJ最短路 - The Shortest Path in Nya Graph HDU - 4725

题意:

给 定 n 个 点 ( 编 号 从 1 到 n ) , 每 个 点 所 在 的 层 次 为 l i , 相 邻 层 次 之 间 有 一 条 权 值 为 C 的 无 向 边 , 给定n个点(编号从1到n),每个点所在的层次为l_i,相邻层次之间有一条权值为C的无向边, n(1n)liC

另 有 m 条 额 外 的 无 向 边 ( u , v , w ) 。 另有m条额外的无向边(u,v,w)。 m(u,v,w)

求 从 1 号 点 到 n 号 点 的 最 短 距 离 。 求从1号点到n号点的最短距离。 1n

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 T200n,m1051C1031u,vn1w1041in

分析:

本 题 难 点 在 于 建 图 。 本题难点在于建图。

如 果 我 们 将 每 一 层 的 结 点 都 存 储 下 来 , 然 后 遍 历 每 一 层 , 将 相 邻 两 层 之 间 的 结 点 两 两 连 接 一 条 边 , 如果我们将每一层的结点都存储下来,然后遍历每一层,将相邻两层之间的结点两两连接一条边,

若 n 个 结 点 平 均 分 布 在 两 层 , 那 么 这 样 建 图 的 复 杂 度 将 达 到 O ( n 2 ) , 在 本 题 是 不 允 许 的 。 若n个结点平均分布在两层,那么这样建图的复杂度将达到O(n^2),在本题是不允许的。 nO(n2)

这 里 采 用 建 立 一 个 虚 拟 ′ ′ 层 结 点 ′ ′ , 来 解 决 这 个 问 题 : 这里采用建立一个虚拟''层结点'',来解决这个问题:

相 邻 层 之 间 所 有 的 点 通 过 一 个 ′ ′ 层 结 点 ′ ′ 建 立 连 接 , 相邻层之间所有的点通过一个''层结点''建立连接,

我 们 对 于 l i 层 , 建 立 一 个 虚 拟 的 结 点 V i , 它 相 当 于 l i 层 的 入 口 , 我们对于l_i层,建立一个虚拟的结点V_i,它相当于l_i层的入口, liVili

V i 与 l i 层 内 的 所 有 结 点 建 立 一 条 有 向 边 , 权 值 为 0 , V_i与l_i层内的所有结点建立一条有向边,权值为0, Vili0

相 邻 层 ( l i ± 1 层 ) 的 所 有 结 点 与 V i 建 立 一 条 有 向 边 , 即 通 过 V i 进 入 到 l i 层 , 权 值 设 置 为 C . 相邻层(l_i±1层)的所有结点与V_i建立一条有向边,即通过V_i进入到l_i层,权值设置为C. (li±1)ViViliC.

这 样 , 我 们 就 能 够 建 出 此 图 。 这样,我们就能够建出此图。

复杂度分析:

n 个 结 点 分 布 在 k 个 不 同 的 层 次 , 那 么 一 共 就 有 n + k 个 结 点 , n个结点分布在k个不同的层次,那么一共就有n+k个结点, nkn+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条边 cntii=1k2×cnti=2i=1kcnti=2n

③ 、 m 条 无 向 边 , 共 需 要 建 立 2 m 条 边 。 ③、m条无向边,共需要建立2m条边。 m2m

故 复 杂 度 为 O ( 3 n + 2 m ) , 因 此 边 的 数 量 上 限 为 5 × 1 0 5 。 在 这 里 T 了 很 多 发 。 故复杂度为O(3n+2m),因此边的数量上限为5×10^5。在这里T了很多发。 O(3n+2m)5×105T

另外: 多 测 注 意 清 空 邻 接 表 指 针 。 多测注意清空邻接表指针。

代码:

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值