[超级码力在线编程大赛初赛(一)] 3.大楼间穿梭 dp入门+单调栈

题目链接:大楼间穿梭
题意

给你一个长度为n的序列,你现在处在下标为1的点,你现在有两个操作向右边行走。

  1. 花费x点体力,移动到右侧k幢建筑中第一栋大于等于当前位置值的点。
  2. 花费y点体力,向右移动一步或者两步。

问最少花费多少体力才能到达最后一个位置上的点。

题解

本题很明显不存在贪心策略使得答案最优,所以这时就需要优雅的暴力dp来帮助我们解决此类问题了。
本题的状态定义和转移很容易定义。
dp[i]:移动到位置i时最少需要多少体力。
那么转态转移由题意得:

d p [ i ] = m i n ( d p [ i ] , m i n ( d p [ i − 1 ] + y , d p [ i − 2 ] + y ) ) ( 由 操 作 2 得 ) {dp[i]=min(dp[i],min(dp[i-1]+y,dp[i-2]+y)) (由操作2得)} dp[i]=min(dp[i],min(dp[i1]+ydp[i2]+y))(2)

d p [ f i [ i ] ] = m i n ( d p [ f i [ i ] ] , d p [ i ] + x ) ( ( f i [ i ] − i ) ≥ k ) ( 由 操 作 1 得 ) {dp[fi[i]]=min(dp[fi[i]],dp[i]+x) ((fi[i]-i) ≥k)(由操作1得)} dp[fi[i]]=min(dp[fi[i]]dp[i]+x)((fi[i]i)k)(1)

( f i [ i ] 是 位 置 i 右 边 第 一 个 大 于 等 于 它 的 下 标 ) {(fi[i]是位置i右边第一个大于等于它的下标)} (fi[i]i)

很容易现在我们只需要解决fi数组就能把问题解决了。
如何找位置i右边的第一个大于等于它的数。

单调栈!!!
我们可以建立一个单调递增的单调栈。每遇到一个小于栈顶的元素入栈,遇到大于等于栈顶的元素则出栈,出栈的每一个元素的fi[i]就是这个大于栈顶这个元素的下标。

代码
long long dp[100010];
int h[100010],fi[100010];
long long inf = 1e18;
class Solution {
public:
	/**
	 * @param heights: the heights of buildings.
	 * @param k: the vision.
	 * @param x: the energy to spend of the first action.
	 * @param y: the energy to spend of the second action.
	 * @return: the minimal energy to spend.
	 */
	long long shuttleInBuildings(vector<int> &heights, int k, int x, int y) {
		// write your code here.
		stack<int> sta;
		while(!sta.empty()) sta.pop();
		int n=heights.size();
		for(int i=0;i<n;i++) h[i+1]=heights[i];
		for(int i=0;i<100010;++i) { dp[i]=inf; fi[i]=-1; }
		for(int i=1;i<=n;i++)
		{
			while(!sta.empty() && h[sta.top()]<=h[i])
			{
				fi[sta.top()]=i;
				sta.pop();
			}
			sta.push(i);
		}
		for(int i=1;i<=n;i++)
		{
			if(i==1) dp[i]=0;
			else dp[i]=min(dp[i],min(dp[i-1]+y, dp[i-2]+y));
			if(fi[i]==-1) continue;
			if(fi[i]-i<=k) dp[fi[i]]=min(dp[fi[i]],dp[i]+x);
		}
		return dp[n];
	}
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值