2017多校四 1005题 hdu 6071 Lazy Running 同余类 最短路

34 篇文章 0 订阅
11 篇文章 0 订阅

题目链接


题意:

给定一个环上的四个点1,2,3,4,距离分别为d12,d23,d34,d41,要求从2号点出发,最后回到2号点,要求经过的距离为大于等于 K 的最小值,求该最小值。


参考:

1. http://blog.csdn.net/blessLZH0108/article/details/76690027?locationNum=6&fps=1  ——Alzh

2. https://post.icpc-camp.org/d/674-poi-x-sums/6  POI X Sums


思路:

详细分析参见链接 2

考虑 dist[i][r] 为走到 i 号点的路径中 % mod = r 的最小值,则 dist[i][r] + mod, dist[i][r] + 2 * mod, ..., dist[i][r] + t * mod 均可以取到,而 dist[i][r] 为 % mod = r 的剩余类中的最小值。这里的 mod 值为 d12 与 d13 中的任一个值的两倍,对上述 均可以取到 的理解即为在距离不够时可以通过来回走这一段来补充距离。

个人觉得 d12 和 d13 随便取哪个都无所谓,之所以取小的不是因为正确性,而是因为空间与时间的考虑(如果说的不对还请麻烦告诉我Orz)

所以我们只需要用最短路处理出所有的 dist[i][j] (1 <= i <= 4, 0 <= j < mod) 的最小值即可。

具体操作时,dist[e.from][dist % mod] + e.dist < dist[e.to][(dist + e.dist) % mod] 时进行松弛。


处理出 dist 数组之后,只需要看 dist[2] 这一维。

如果 dist[2][i] <= k, 说明 dist[2][i] 可以通过加上若干个 mod 去得到大于等于 k 的最小值,反过来也就是 k 通过减去若干个 mod 可以得到 0 ~ mod - 1 范围内的一个值,不妨记为 kmod = k % mod, 那么 i >= kmod 时,i - kmod 即为超出的部分;否则 mod - (kmod - i) 即为超出的部分(想想也很直观);

如果 dist[2][i] > k, 那么超出的部分即为 dist[2][i] - k,

最后所有超出的部分取个最小值即为答案。


Code:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
typedef long long LL;
using namespace std;
struct qnode {
    LL src, r;
    LL dist;
    qnode(LL _src = 0, LL _r = 0, LL _d = 0) : src(_src), r(_r), dist(_d) {}
    bool operator < (const qnode& q) const { return dist > q.dist; }
};
struct Edge {
    LL to, ne; LL dist;
    Edge(LL v = 0, LL d = 0, LL next = 0) : to(v), dist(d), ne(next) {}
}edge[10];
LL tot, ne[10], d[10];
void add(LL u, LL v, LL d) {
    edge[tot] = Edge(v, d, ne[u]);
    ne[u] = tot++;
}
priority_queue<qnode> q;
LL dist[5][60010], k;
inline LL min(LL a, LL b) { return a < b ? a : b; }
void bfs(LL src) {
    LL mod = 2LL * min(d[1], d[2]);
    for (LL i = 1; i <= 4; ++i) {
        for (LL j = 0; j < mod; ++j) dist[i][j] = inf;
    }
    q.push(qnode(src, 0, 0)); dist[2][0] = 0;
    while (!q.empty()) {
//        printf("%d\n", q.size());
        qnode nowf = q.top(); q.pop();
        LL u = nowf.src, x = nowf.r;
        if (nowf.dist > dist[u][x] || nowf.dist >= k + mod) continue;
        for (LL i = ne[u]; i != -1; i = edge[i].ne) {
            Edge e = edge[i]; LL v = e.to, nx = (x + e.dist) % mod;
            if (dist[u][x] + e.dist < dist[v][nx]) {
                dist[v][nx] = dist[u][x] + e.dist;
                q.push(qnode(v, nx, dist[v][nx]));
            }
        }
    }
    LL kmod = k % mod;
    LL diff = mod;
    for (LL i = 0; i < mod; ++i) {
        if (dist[2][i] <= k) {
            if (i >= kmod) diff = min(diff, (LL)i - kmod);
            else diff = min(diff, (LL)mod - (kmod - i));
        }
        else diff = min(diff, dist[2][i] - k);
    }
    printf("%lld\n", k + diff);
}
void work() {
    memset(ne, -1, sizeof(ne)); tot = 0;
    scanf("%lld%lld%lld%lld%lld", &k, &d[1], &d[2], &d[3], &d[4]);
    for (LL i = 1; i <= 4; ++i) {
        LL from = i, to = i % 4 + 1;
        add(from, to, d[i]); add(to, from, d[i]);
    }
    bfs(2);
}
int main() {
    LL T;
    scanf("%lld", &T);
    while (T--) work();
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值