zoukankan      html  css  js  c++  java
  • 暑假集训Day13 升降梯上

    题目大意

    开启了升降梯的动力之后,探险队员们进入了升降梯运行的那条竖直的隧道,映入眼帘的是一条直通塔顶的轨道、一辆停在轨道底部的电梯、和电梯内一杆控制电梯升降的巨大手柄。

    Nescafe之塔一共有N层,升降梯在每层都有一个停靠点。手柄有M个控制槽,第i个控制槽旁边标着一个数Ci,满足C1<C2<C3<……<CM。如果Ci>0,表示手柄扳动到该槽时,电梯将上升Ci层;如果Ci<0,表示手柄扳动到该槽时,电梯将下降-Ci层;并且一定存在一个Ci=0,手柄最初就位于此槽中。注意升降梯只能在1~N层间移动,因此扳动到使升降梯移动到1层以下、N层以上的控制槽是不允许的。

    电梯每移动一层,需要花费2秒钟时间,而手柄从一个控制槽扳到相邻的槽,需要花费1秒钟时间。探险队员现在在1层,并且想尽快到达N层,他们想知道从1层到N层至少需要多长时间?

    输入格式

    第一行两个正整数N、M。

    第二行M个整数C1、C2……CM。

    输出格式

    输出一个整数表示答案,即至少需要多长时间。若不可能到达输出-1。

    样例

    样例输入

    6 3
    -1 0 2
    

    样例输出

    19
    

    算法分析

    • 莫名用奇怪的dp 就给A掉了 正解是最短路(奇怪的最短路增加了)
    • 来讲讲我奇怪的dp吧
    • 定义一个数组f[i][j]表示当前在第i层第j个按钮出需要最少的时间
    • 然后枚举状态的时候用三层循环 第一层枚举i 第二层枚举j 第三层枚举k作为上次是在哪个按钮
    • 然后转移方程就很简单了 直接搞起就好了
    • 但是这样的话为啥要叫奇怪的dp呢?因为我们如果直接这样转移的话 会发现不对(输出会是我们初始化的0x3f3f3f3f)所以我们要做一些猥琐很妙的事情:多跑几遍!
    • 没错宁木得看错就是多跑几遍 关于为啥多跑几遍就对了呢? 其实我们很容易就可以想到 : 因为我们既可以上楼也可以下楼 所以如果我们无论正着跑 倒着跑 显然都是不对的 (dalao可以尝试加上一堆限制条件和特盘与初始化),那该咋办呢,那我们就既正着跑 也反着跑 然后再正着跑 这样的话将前两次的作为第三次跑的初始值就木得问题啦(复杂度的话也不用太担心 毕竟这样除了一些丧心病狂优美异常的卡常题以外 还是可以过去的 毕竟多跑两遍也就是常数的变化)
    • 下面看代码吧

    代码展示

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn = 1e3+10;
    int n,m,dp[maxn][maxn],dp2[maxn][maxn],c[maxn];
    int ans=0x3f3f3f3f;
    int x0;
    
    int main(){
    	scanf("%d%d",&n,&m);
    	for(int i = 1;i <= m;++i){
    		scanf("%d",&c[i]);//输出各个按钮的上下楼
    		if(c[i] == 0)x0 = i;//找到起点
    	}
    	memset(dp,0x3f,sizeof(dp));//初始化
    	dp[1][x0]=0;//初始化,在一层x0的位置显然是0
    	for(int i = 1;i <= m;++i)dp[1][i] = abs(i-x0);//还是初始化
    	for(int i = 2;i <= n;++i){//从第二层跑到第n层
    		for(int j = 1;j <= m;++j){//枚举按钮
    			for(int k = 1;k <= m;k++){//上个按钮
    				if(i - c[j] < 1 || i - c[j] > n)continue;//不满足条件的情况
    				dp[i][j] = min(dp[i][j],dp[i - c[j]][k] + abs(j-k) + abs(c[j]) * 2);//开始dp
    			}	
    		}
    	}
    	for(int i = n;i >= 2;--i){//倒着跑一遍 (这不就操作起来了嘛)
    		for(int j = 1;j <= m;++j){//同第一个
    			for(int k = 1;k <= m;k++){
    				if(i - c[j] < 1 || i - c[j] > n)continue;
    				dp[i][j] = min(dp[i][j],dp[i - c[j]][k] + abs(j-k) + abs(c[j]) * 2);
    			}	
    		}
    	}
    	for(int i = 2;i <= n;++i){//操作就完了呗
    		for(int j = 1;j <= m;++j){
    			for(int k = 1;k <= m;k++){
    				if(i - c[j] < 1 || i - c[j] > n)continue;
    				dp[i][j] = min(dp[i][j],dp[i - c[j]][k] + abs(j-k) + abs(c[j]) * 2);
    			}	
    		}
    	}
    	for(int i = 1;i <= m;++i)ans=min(ans,dp[n][i]);//寻找区间最值
    	if(ans == 0x3f3f3f3f)printf("-1
    ");//到不了第n层
    	else cout<<ans<<endl;
    	return 0;
    }
    
  • 相关阅读:
    通信错误:(-1)[描述:无法解析路由器DDNS地址,请检查DDNS状态.] 解析办法
    小数量宽带用户的福音,Panabit 云计费easyradius 接口隆重发布,PA宽带计费系统
    送给那些经常问我如何设置360测速结果为电信的朋友,360测速模块原理简单分析
    Universal-Image-Loader(UIL)使用方法&流程图&源码分析 ----- 未完
    Android开发框架
    Android线程池的使用(未完)
    Android LruCache究竟是什么
    Java finally语句到底是在return之前还是之后执行?
    Android自定义图片加载框架
    Android 自定义View实现单击和双击事件
  • 原文地址:https://www.cnblogs.com/2004-08-20/p/13251524.html
Copyright © 2011-2022 走看看