【HDOJ6071】Lazy Running(同余最短路思想)

传送门

  • 题目:
    四个点 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4顺序连成环,给定相邻两点间的距离,问从 2 2 2号点出发再回到 2 2 2号点的所有路径中,路径长度不小于 k k k的最小值。输出这个最小值。 1 ≤ k ≤ 1 e 18 1≤k≤1e18 1k1e18
  • 思路:
    主要用到同余的思想。
    w = m i n ( d i s 12 , d i s 23 ) w=min(dis_{12},dis_{23}) w=min(dis12,dis23)对于 2 → 2 2\rightarrow2 22的一条路径,记其路径长度为 l e n len len,那么 l e n + k ∗ 2 w len+k*2w len+k2w ( k ≥ 0 ) (k≥0) (k0)对应的路径也是一种合法路径,这些合法路径长度根据 % 2 w \%2w %2w的值将其分为 2 w 2w 2w组,并且求出每个同余类 i i i对应的最短的合法路径的长度 m l e n [ i ] mlen[i] mlen[i]
    m l e n [ i ] mlen[i] mlen[i]表示从 2 → 2 2\rightarrow2 22的合法路径中(路径长度为len),满足 l e n % ( 2 w ) = i len\%(2w)=i len%(2w)=i的最小的 l e n len len值。
    对于每个同余类 i i i,如果 m l e n [ i ] ≥ k mlen[i]≥k mlen[i]k那么更新答案,否则,在 m l e n [ i ] mlen[i] mlen[i]的基础上累加 2 w 2w 2w,直至满足长度 ≥ k ≥k k,再更新答案。
    那么怎么获得 m l e n [ ] mlen[] mlen[]数组呢,可以暴搜,也可以用spfa做。
    用spfa做的话 d i s [ i ] [ j ] dis[i][j] dis[i][j]表示从 2 2 2出发到达 i i i满足 % ( 2 w ) = j \%(2w)=j %(2w)=j的最小长度。队列里存上一个点距离当前点最短距离,初始化存入{ 2 , 0 2,0 2,0}。
  • ac代码:
#include <iostream>
#include <vector>
#include <limits.h>
#include <queue>
using namespace std;
typedef long long ll;
const int maxn = 6e4+10;
struct Edge{
    int nxt; ll val;
};
struct node{
    int now; ll v;
};
vector<Edge> edge[5];
queue<node> q;
int t;
ll d12, d23, d34, d41, w, k, dis[5][maxn];
void spfa()
{
    for(int i = 0; i <= 4; i++)
        for(int j = 0; j < w; j++)
            dis[i][j] = LLONG_MAX;
    q.push({2, 0});
    while(!q.empty())
    {
        int now = q.front().now; ll v = q.front().v; q.pop();
        for(auto e: edge[now])
        {
            ll tmp = v+e.val;
            if(dis[e.nxt][tmp%w]>tmp)
            {
                dis[e.nxt][tmp%w] = tmp;
                q.push({e.nxt, tmp});
            }
        }
    }
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    scanf("%d", &t);
    while(t--)
    {
        scanf("%lld %lld %lld %lld %lld", &k, &d12, &d23, &d34, &d41);
        w = 2*min(d12, d23);
        edge[1].push_back({2, d12}); edge[2].push_back({1, d12});
        edge[2].push_back({3, d23}); edge[3].push_back({2, d23});
        edge[3].push_back({4, d34}); edge[4].push_back({3, d34});
        edge[4].push_back({1, d41}); edge[1].push_back({4, d41});
        spfa();
        ll ans = LLONG_MAX;
        for(int i = 0; i < w; i++)
        {
            if(dis[2][i]>=k) ans = min(ans, dis[2][i]);
            else
            {
                ll sub = k-dis[2][i];
                ll p = sub%w==0 ? sub : (sub/w+1)*w;
                ans = min(ans, dis[2][i]+p);
            }
        }
        printf("%lld\n", ans);
        for(int i = 1; i <= 4; i++) edge[i].clear();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值