洛谷 P1095 守望者的逃离(贪心,空间压缩DP)

探讨了在有限时间内,如何合理利用初始魔力值,通过不同动作选择,达到尽可能远的距离。文章提出了一种动态规划算法,通过状态压缩优化空间复杂度,实现了对问题的有效求解。

题目大意:

我们控制一个人,他的初始魔力值为M。每一秒他有三个动作,分别是:不耗魔继续往前跑17米,或者耗10点魔力使用闪烁往前60米,或者停止不动魔加4点。

现在给定时间T,问是否能走到S

已知M,S,T。其中M代表魔力值,S代表距离,T代表时间。

解题思路:

首先,我们可以容易看出,若初始有魔的话,最好让魔只剩m%=10,因为耗完所有闪烁次数是最合理的。

然后,我们看到若状态为

state=[time][mona],其中time代表还剩多少时间,mona为还剩多少魔力值,表示还剩time,mona时最远能走多少,那么我们可以得到

if mona<10

memo[time][mona]= max(memo[time-1][mona+4],memo[time-1][mona]+17)

else

memo[time][mona]= max(max(memo[time-1][mona+4],memo[time-1][mona]+17),memo[time-1][mona-10]+60)

其中边界条件为:

if mona>=10

memo[1][mona]=60

else

memo[1][mona]=17

但是,我们发现memo空间为1e8,超过了空间复杂度1e7,那么我们要进行空间压缩,我们发现每一个时刻的状态只和下一个时刻的状态有关,所以[time]这一栏可以变为[2],即

state=[2][mona]

#include <bits/stdc++.h>
using namespace std;
const int MAXM = 1020;
int memo[2][MAXM];

int main(){
	int m,s,t;
	cin>>m>>s>>t;
	int times=m/10;
	m=m%10;
	if(times<=t){
		if(times*60 >=s){
			cout<<"Yes"<<endl;
			double ans=ceil((double)s/60);
			cout<<(int)ans<<endl;
			exit(0);
		}
		else{
			s-=times*60;
			t-=times;
		}
	}else{
		if(t*60>=s){
			cout<<"Yes"<<endl;
			double ans=ceil((double)s/60);
			cout<<(int)ans<<endl;
			exit(0);
		}
		else{
			cout<<"No"<<endl;
			cout<<t*60<<endl;
			exit(0);
		}
	}
	
	int cur=1;
	
	memset(memo,0,sizeof(memo));
	
	for(int time=1;time<=t;time++){
		cur=!cur;
		for(int mona=0;mona<=m+20;mona++){
			if(time==1){
				if(mona>=10)
				memo[cur][mona]=60;
				else memo[cur][mona]=17;
				continue;
			}
			if(mona<10){
				memo[cur][mona]=max(memo[!cur][mona]+17,memo[!cur][mona+4]);
			}else{
				memo[cur][mona]=max(memo[!cur][mona]+17,memo[!cur][mona-10]+60);
				memo[cur][mona]=max(memo[cur][mona],memo[!cur][mona+4]);
			}
			}
		if(memo[cur][m]>=s){
			cout<<"Yes"<<endl;
			cout<<time+times<<endl;
			exit(0);
		}
	}
	
	
	cout<<"No"<<endl;
	cout<<memo[cur][m]+times*60<<endl;
	return 0;

}

 

### P1095 守望者逃离 Java 实现 #### 问题描述 守望者被围困在一个即将沉没的荒岛上,需要尽快逃离。给定守望者的魔法初值 \( M \),初始位置与岛的出口之间的距离 \( S \),以及岛沉没的时间 \( T \)守望者可以通过跑步、闪烁或休息来行动,每种活动都以秒为单位。 - 跑步速度:17 m/s - 闪烁法术:可以在1 s内移动60 m,消耗魔法值10点 - 魔法恢复速率:4点/s(仅在静止状态下) 目标是在最短时间逃离岛屿;如果无法逃脱,则计算守望者能够行走的最大距离[^2]。 #### 解决思路 该问题是典型的动态规划 (Dynamic Programming, DP) 类型题目。通过构建二维数组 `dp[i][j]` 来表示第 i 秒时剩余 j 点魔力的情况下所能达到的最大位移量。对于每一时刻的状态转移方程如下: - 如果选择跑动,则下一状态为 dp\[i+1\]\[j\]=max(dp\[i+1\]\[j\],dp\[i\]\[j\]+17); - 若决定瞬移,则更新规则变为 dp\[i+1\]\[j−10\]=max(dp\[i+1\]\[j−10\],dp\[i\]\[j\]+60),前提是当前魔力不少于10; - 当处于休憩模式下,魔力逐渐回复至上限但不超出原有数值,即 dp\[i+1\]\[min(j+4,M)\]=max(dp\[i+1\]\[min(j+4,M)\],dp\[i\]\[j\]). 最终解应取自所有可能终态中的最大值作为答案返回。 #### Java代码实现 ```java import java.util.Scanner; public class Escape { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int M = scanner.nextInt(); // 初始魔力值 int S = scanner.nextInt(); // 出口的距离 int T = scanner.nextInt(); // 岛屿下沉时间 final int RUN_SPEED = 17; // 跑步速度 final int TELEPORT_DISTANCE = 60; // 传送距离 final int MAGIC_COST_TELEPORT = 10; // 使用一次传送所需的魔力 final int MAGIC_RECOVER_RATE = 4; // 每秒钟恢复的魔力量 boolean canEscape = false; int maxDistanceCovered = 0; // 动态规划表初始化 int[][] dp = new int[T + 1][M + 1]; for(int t=0;t<=T;++t){ for(int magicLeft=0;magicLeft<=Math.min(M,t*MAGIC_RECOVER_RATE);++magicLeft){ if(t>0 && magicLeft>=MAGIC_COST_TELEPORT){ dp[t][magicLeft-MAGIC_COST_TELEPORT] = Math.max( dp[t][magicLeft-MAGIC_COST_TELEPORT], dp[t-1][magicLeft] + TELEPORT_DISTANCE ); } if(t>0){ dp[t][magicLeft] = Math.max( dp[t][magicLeft], dp[t-1][magicLeft] + RUN_SPEED ); if(magicLeft<M){ dp[t][magicLeft+MAGIC_RECOVER_RATE] = Math.max( dp[t][magicLeft+MAGIC_RECOVER_RATE], dp[t-1][magicLeft] ); } } if(!canEscape && dp[t][magicLeft]>=S){ System.out.println("可以成功逃跑"); canEscape=true; }else{ maxDistanceCovered=Math.max(maxDistanceCovered,dp[t][magicLeft]); } } } if (!canEscape) { System.out.printf("无法逃生,在规定时间内可前进的最大距离:%d 米\n", maxDistanceCovered); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值