Mortal Kombat Tower (dp动态规划)

题意

A和B两人玩通关游戏,每个关卡分为难(a[i]=1)和简单(a[i]=0),A可以通过任意难度的关卡,B只能通过难度为0的关卡,但是B在遇到难度为1的关卡的时候,可以选择跳跃一次,A和B轮流交替通关,每一轮两人最少可以通关一关,最多通关两关,B始终第一个开始,问:最终通关时,B进行跳跃的最少次数

题目传传送门:点击进入

思路

由于是需要求一个最优解,同时这个最优解由AB两人的选择控制,为了维护这个结果最优,需要每个回合两人的选择都是最优的,同时A的选择还会影响到B的选择。所以我们考虑动态规划进行求解。

求解动态规划的四个步骤

一、确定状态

设数组dp[i][0]表示第i轮中 B消耗的跳跃次数, dp[i][1]表示第i轮中,A消耗的跳跃次数。

两个意识:

1.最后一步

即最终的一个状态。本题中的最后一个状态应该是min( dp[n][0], dp[n][1] )

2.子问题

由于我们的每一轮的最终答案是由上一轮的结果来决定,所以我们可以将每个问题化解成它的子问题,在本题中,我们考虑走到最后一关的最优解,则可以将问题拆解为最后一关的最优解+前面通过的所有关卡的最优解,不断拆分,就可以将问题简化为我们已知的问题。

二、 状态转移方程

对于dp问题,最关键的就是推出该问题的状态转移方程,在本题中,我们设数组dp[i][0]表示第i轮中 B消耗的跳跃次数,
dp[i][1]表示第i轮中,A消耗的跳跃次数。由于AB两人在自己的回合可以选择通过1关或者2关,所以我们需要每一轮都选择二者中消耗跳跃步数最少的一个决策,以确保最后的决策是最优的。

对于第i回合的B来说:
选择通1关:
dp[i][0] = dp[i-1][1] + a[i];

选择通2关:因为是通两关,所以要加上a[i-1]和当前的关卡a[i]
dp[i][0] = dp[i-2][1] + a[i-1] + a[i]

第i轮B的最优策略为:dp[i][0] = min( dp[i-1][1] + a[i],dp[i-2][1] + a[i-1] + a[i] )

对于第i回合的A来说
选择通1关:

dp[i][1] = dp[i-1][0]

选择通2关:

dp[i][1] = dp[i-2][0]

第i轮A的最优策略为:dp[i][1] = min( dp[i-1][0], dp[i-2][0])

三、初始条件和边界情况

边界情况在本题中不需要考虑。初始条件为:

第一轮:dp[1][0] = a[1] dp[1][1] = 无限大(因为B始终走第一轮)

第二轮:dp[2][0] = a[1] + a[2] dp[2][1] = dp[1][0]

四、计算顺序

一般来说按照从小到大,从左往右,或者对于二维数组来说一行一行的进行计算。

AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <math.h>
#include <set>
#include <map>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 2e5 + 5;
typedef long long ll;
const int INF = 0x3f3f3f3f;
int a[maxn];
int dp[maxn][2];
int main() {
	int t = 0;
	cin >> t;
	while(t--){
		int n;
		cin >> n;
		for(int i = 1; i <= n; i++){
			cin >> a[i];
		}
		dp[1][0] = a[1];
		dp[1][1] = INF;

		dp[2][0] = a[1] + a[2];  //前两个数
		dp[2][1] = dp[1][0];

		for(int i = 3; i <= n; i++){
			dp[i][0] = min(dp[i-1][1] + a[i],dp[i-2][1] + a[i] + a[i-1] + a[i]);
			dp[i][1] = min(dp[i-1][0],dp[i-2][0]);
		}
		cout << min(dp[n][0],dp[n][1]) << endl; 
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,你的问题是关于一个类层次结构的定义。这里给出一个 Java 代码示例: ```java // 定义人类,具有基本的吃和睡行为 class Human { public void eat() { System.out.println("Human eat"); } public void sleep() { System.out.println("Human sleep"); } } // 定义可以做梦的行为物体 interface Dreamable { public void dream(); } // 定义可以飞行的行为物体 interface Flyable { public void fly(); } // 定义超人类,继承自人类,具有飞行技能 class Superman extends Human implements Flyable { public void fly() { System.out.println("Superman fly"); } } // 定义凡人类,继承自人类,具有做梦技能 class Mortal extends Human implements Dreamable { public void dream() { System.out.println("Mortal dream"); } } // 定义大鸟类,实现飞行接口 class Eagle implements Flyable { public void fly() { System.out.println("Eagle fly"); } } // 测试类 public class Test { public static void main(String[] args) { // 构建凡人对象 Mortal mortal = new Mortal(); mortal.eat(); mortal.sleep(); mortal.dream(); // 构建会飞的对象数组 Flyable[] flyables = new Flyable[] {new Eagle(), new Superman()}; // 打印数组中各对象的状态 for (Flyable f : flyables) { if (f instanceof Eagle) { System.out.println("This is an Eagle."); } else if (f instanceof Superman) { System.out.println("This is a Superman."); } f.fly(); } } } ``` 注意,在 `Test` 类的 `main` 方法中,我们首先构建了一个凡人对象 `mortal`,并调用了其吃、睡、做梦的方法。然后,我们构建了一个会飞的对象数组 `flyables`,其中包含一个大鸟对象和一个超人对象。我们通过遍历数组中的每个元素,并使用 `instanceof` 运算符判断其类型,来打印数组中各对象的状态。最后,我们调用每个对象的飞行方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值