HDU 6071 Lazy Running (Dijstra, 2017 Multi-Univ Training Contest 4)

Problem

四个打卡点 p1,p2,p3,p4 。已知 d1,2,d2,3,d3,1,d1,4 的距离(可双向连通),其它任意两点间均无直接路径。求从 2 出发,任意通过路径回到 2 号点使得总距离恰好 K 的最小距离是多少?

Limit

1K1018

1d30000

Idea

由于每条路径都可以无限次通过,故考虑可以在 1, 2 间往返跑,每次对距离的贡献为 2×d1,2 ,则对任意从 2 到 2 的可能路径长度 L, 必然能得到 L+2d1,2×p (其中 p 为任意正整数)。故 L 对 2d1,2 取模不会影响结果的判断, L+2d1,2×p1=L%(2d1,2)+2d1,2×p2 必然成立。故可以用 dijstra 在记录 当前点号,总距离,距离 mod 2*d[1][2] 数据下,不断记录在取模距离为 dist 的情况下,是否可以到达点 i 的方案 vis[dist][i] 。此处的总复杂度为 O(d1,2×logd1,2)

对其中的每种可行距离方案 dist , dist - K % (2*d[1][2]) + K 求最小 K 即可。

但仍需考虑一种情况,当总距离>= K 且到达 2 时,此时不应取模处理,而应直接记录最小值并停止其后续步骤。

Code

#include<bits/stdc++.h>
using namespace std;
int T, dis[5][5], nxt[5][2] = {0,0, 4,2, 1,3, 2,4, 3,1};
bool vis[60010][5];
long long k, K;
struct Node {
    int cur;
    long long dist, distmod;
    Node(){}
    Node(int _cur, long long _dist, long long _distmod):cur(_cur), dist(_dist), distmod(_distmod){}
} p, q;
bool operator<(Node a, Node b) {
    return a.distmod > b.distmod;
}
long long dijstra() {
    long long mod = dis[1][2] * 2;
    if(K % mod == 0)    return K;
    long long directAns = (K/dis[1][2] + 1ll) * dis[1][2];
    memset(vis, 0, sizeof(vis));
    k = K % mod; 
    priority_queue<Node> que;
    que.push(Node(2, 0, 0));
    vis[0][2] = 1;
    while(!que.empty())
    {
        p = que.top();
        que.pop();
        if(p.dist > directAns)  break;
        for(int i=0;i<2;i++)
        {
            q.cur = nxt[p.cur][i];
            q.dist = p.dist + dis[p.cur][q.cur];
            if(q.dist > K)
                q.distmod = q.dist;
            else
                q.distmod = q.dist % mod;
            if(q.dist > K && q.cur == 2) {
                directAns = min(directAns, q.dist);
                continue;
            }
            if(vis[q.distmod][q.cur])   continue;
            if(q.dist <= K) 
                vis[q.distmod][q.cur] = 1;
            que.push(q);
        }
    }
    for(int i=k;i<mod;i++)
        if(vis[i][2])   return min(directAns, i-k+K);
    return directAns;
}
int main()
{
    scanf("%d", &T);
    while(T-- && scanf("%lld", &K))
    {
        for(int i=1;i<=4;i++)
        {
            scanf("%d", &dis[i][i==4?1:i+1]);
            dis[i==4?1:i+1][i] = dis[i][i==4?1:i+1];
        }
        printf("%lld\n", dijstra());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值