HDU 6071 Lazy Running

  • 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6071

  • 题意:

    • 环上有4个点,给定相邻点的距离,找一条以2号点起止的路径,使总长度大于K,求路径的最小长度。
  • 规模:

    • T(1≤T≤15)
    • K,d1,2,d2,3,d3,4,d4,1(1≤K≤10^18,1≤d≤30000)
  • 类型:

    • DP
  • 分析:

    • 初看题目,应该是一个DP问题,DP[i][j],i为当前节点编号,j为当前已走长度,那么转移方程是很好写的。
    • 但是对于这道题,K太大了,状态数太多。考虑优化;
    • 取w=min(d_{1,2},d_{2,3}),那么对于每一种方案,均可以通过往返跑w这条边使得距离增加2w。也就是说,如果存在距离为k的方案,那么必然存在距离为k+2w的方案。
    • 这个2w其实是跑一圈的最短长度;
    • 这个时候考虑K,2w同余系,如果vis[1][i]==false,即任意跑法都不能得到长度(i+2w),舍去
    • 需要记录能够i的最小dis;
  • 时间复杂度&&优化:

    • O(4*2*d)
  • 代码:

#include <bits/stdc++.h>
using namespace std;

const int MAXN=70000;
const int INF=0x3fff3fff;

typedef long long ll;

using namespace std;

ll K;
int x1,x2,x3,x4;
bool vis[5][MAXN];
ll dis[5][MAXN];

struct node{
    int pos;
    int dis;
    node(int _pos=0,int _dis=0):pos(_pos),dis(_dis){}
};

int edge[4][4];

void init(){
    memset(edge,-1,sizeof(edge));
}

ll DP(ll MOD){
    memset(vis,false,sizeof(vis));
    memset(dis,0,sizeof(dis));
    queue<node > q;
    while(!q.empty())q.pop();
    q.push(node(1,0));
    vis[1][0]=true;
    while(!q.empty()){
        node tmp=q.front();q.pop();
//        cout<<tmp.pos<<"    "<<dis[tmp.pos][tmp.dis]<<endl;
        node next;
        for(int i=0;i<4;i++){
            if(edge[tmp.pos][i]==-1)continue;
            next.pos=i;
            next.dis=(tmp.dis+edge[tmp.pos][i])%MOD;
            if(!vis[next.pos][next.dis]||dis[next.pos][next.dis]>dis[tmp.pos][tmp.dis]+edge[tmp.pos][i]){
                vis[next.pos][next.dis]=true;
                dis[next.pos][next.dis]=dis[tmp.pos][tmp.dis]+edge[tmp.pos][i];
                //dis[next.pos][next.dis]=(max((ll)0,K-next.dis-1)/MOD +1)*MOD+next.dis;
                q.push(next);
            }
        }
    }
    ll x,y,ret=-1;
    for(int i=0;i<MOD;i++){
        if(vis[1][i]==true){
            x=dis[1][i];
            if(x>=K)y=x;
            else {
                y=((K-x-1)/MOD+1)*MOD+x;
            }
            ret==-1?ret=y:ret=min(ret,y);
        }
    }
    return ret;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%I64d%d%d%d%d",&K,&edge[0][1],&edge[1][2],&edge[2][3],&edge[3][0]);
        edge[1][0]=edge[0][1];
        edge[2][1]=edge[1][2];
        edge[3][2]=edge[2][3];
        edge[0][3]=edge[3][0];

        ll MOD=min(2*edge[0][1],2*edge[1][2]);
        printf("%I64d\n",DP(MOD));
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值