AcWing 312. 乌龟棋(每日一题)

2024-04-06 08:04
文章标签 每日 acwing 乌龟 312

本文主要是介绍AcWing 312. 乌龟棋(每日一题),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原题链接:312. 乌龟棋 - AcWing题库

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。

乌龟棋的棋盘只有一行,该行有 N 个格子,每个格子上一个分数(非负整数)。

棋盘第 1 格是唯一的起点,第 N 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。

乌龟棋中共有 M 张爬行卡片,分成 4 种不同的类型(M 张卡片中不一定包含所有 4 种类型的卡片),每种类型的卡片上分别标有 1、2、3、4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。

游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次

游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。

玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。

很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。

现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?

输入格式

输入文件的每行中两个数之间用一个空格隔开。

第 1 行 2 个正整数 N 和 M,分别表示棋盘格子数和爬行卡片数。

第 2 行 N 个非负整数,a1,a2,……,aN,其中 ai 表示棋盘第 i 个格子上的分数。

第 3 行 M 个整数,b1,b2,……,bM,表示 M 张爬行卡片上的数字。

输入数据保证到达终点时刚好用光 M 张爬行卡片。

输出格式

输出只有 1 行,包含 1 个整数,表示小明最多能得到的分数。

数据范围

1≤N≤350,
1≤M≤120,
0≤ai≤100,
1≤bi≤4,
每种爬行卡片的张数不会超过 40。

输入样例:

9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1

输出样例:

73

解题思路:

此题标签是线性DP,暴力只可过两个样例点,此题DP状态的定义和描述是四维的定义,f[i][j][k][l]数组表示步数1用了i张步数2用了j张步数3用了k张步数4用了l张所获得最大分数,那么可以枚举步数的每一个状态,数组s统计卡牌步数的个数,最后求一下f[s[1]][s[2]][s[3]][s[4]],既每一张卡牌都用完,那么到达任意一个格子,状态转移可以又四个状态转移过来,f[i][j][k][l]=max(f[i-1][j][k][l],f[i][j-1][k][l],f[i][j][k-1][l],f[i][j][k][l-1])+当前值。即到达f[i][j][k][l],可以先走f[i-1][j][k][l],留一张步数为1的卡牌留到最后使用到达此时的点(1+i+2*j+3*k+4*l),同理f[i][j-1][k][l]为留一张步数为2的卡牌到最后一步使用到达(1+i+2*j+3*k+4*l)点……



代码实现:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N =355,M=45;
int n,m;
int a[N],s[N];//a是分数数组,s[i]表示跳i步卡牌有s[i]张
int f[M][M][M][M];
//f[i][j][k][l]数组表示步数1用了i张步数2用了j张步数3用了k张步数4用了l张所获得最大分数
int main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=m;i++){int x;cin>>x;s[x]++;}f[0][0][0][0]=a[1];//初始为第一个格子,分数为第一个格子for(int A=0;A<=s[1];A++){//分别枚举卡牌1,2,3,4的个数for(int B=0;B<=s[2];B++){for(int C=0;C<=s[3];C++){for(int D=0;D<=s[4];D++){int score=a[1+A+2*B+3*C+4*D];//可以到达点的分数//前一个状态可以由少用1,2,3,4步的一张卡牌转移if(A) f[A][B][C][D]=max(f[A][B][C][D],f[A-1][B][C][D]+score);if(B) f[A][B][C][D]=max(f[A][B][C][D],f[A][B-1][C][D]+score);if(C) f[A][B][C][D]=max(f[A][B][C][D],f[A][B][C-1][D]+score);if(D) f[A][B][C][D]=max(f[A][B][C][D],f[A][B][C][D-1]+score);}}}}cout<<f[s[1]][s[2]][s[3]][s[4]]<<endl;return 0;
}

总结:

此种状态定义博主第一次遇到,最多的也就见过三维解决问题,像李白打酒,这个题状态的定义与描述很难想,开始寻思暴力能不能多拿点分,只能过两个样例,^—^,后来看了y总讲解,才知道用四维去定义,还是要多做题,DP问题只要能找到状态转移方程就基本解决了,博主感觉最难的还是状态的定义与描述。多做题积累经验,文章代码实现或者思路有错误的地方,请各位大佬指出,感激不尽*~*。

PS:上文两张图片均来自y总讲解 作者:yxc

这篇关于AcWing 312. 乌龟棋(每日一题)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/879424

相关文章

uva 10154 DP 叠乌龟

题意: 给你几只乌龟,每只乌龟有自身的重量和力量。 每只乌龟的力量可以承受自身体重和在其上的几只乌龟的体重和内。 问最多能叠放几只乌龟。 解析: 先将乌龟按力量从小到大排列。 然后dp的时候从前往后叠,状态转移方程: dp[i][j] = dp[i - 1][j];if (dp[i - 1][j - 1] != inf && dp[i - 1][j - 1] <= t[i]

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

每日一练7:简写单词(含链接)

1.链接 简写单词_牛客题霸_牛客网 2.题目 3.代码1(错误经验) #include <iostream>#include <string>using namespace std;int main() {string s;string ret;int count = 0;while(cin >> s)for(auto a : s){if(count == 0){if( a <=

【每日刷题】Day113

【每日刷题】Day113 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. 91. 解码方法 - 力扣(LeetCode) 2. LCR 098. 不同路径 - 力扣(LeetCode) 3. 63. 不同路径 II - 力扣(LeetCode) 1. 91. 解码方法 - 力扣(LeetCode) //思路:动态规划。 cl

【AcWing】851. 求最短路

spfa算法其实是对贝尔曼福特算法做一个优化。 贝尔曼福特算法会遍历所有边来更新,但是每一次迭代的话我不一定每条边都会更新,SPFA是对这个做优化。 如果说dist[b]在当前这次迭代想变小的话,那么一定是dist[a]变小了,只有a变小了,a的后继(b)才会变小。 用宽搜来做优化,用一个队列,队列里边存的就是所有变小了的结点(队列里存的是待更新的点)。 基本思路就是我更新过谁,我再拿

力扣 739. 每日温度【经典单调栈题目】

1. 题目 理解题意: 1.1. 给一个温度集合, 要返回一个对应长度的结果集合, 这个结果集合里面的元素 i 是 当前 i 位置的元素的下一个更高温度的元素的位置和当前 i 位置的距离之差, 若是当前元素不存在下一个更高温度的元素, 则这个位置用0代替; 2. 思路 本题用单调栈来求解;单调栈就适用于来求当前元素左边或者右边第一个比当前元素大或者小的元素;【单调栈:让栈中的元素保持单调

每日一题——第八十一题

打印如下图案: #include<stdio.h>int main() {int i, j;char ch = 'A';for (i = 1; i < 5; i++, ch++){for (j = 0; j < 5 - i; j++){printf(" ");//控制空格输出}for (j = 1; j < 2 * i; j++)//条件j < 2 * i{printf("%c", ch

每日一题,力扣leetcode Hot100之238.除自身以外数组的乘积

乍一看这个题很简单,但是不能用除法,并且在O(N)时间复杂度完成或许有点难度。 考虑到不能用除法,如果我们要计算输出结果位置i的值,我们就要获取这个位置左边的乘积和右边的乘积,那么我新设立两个数组L和R。 对于L来说,由于表达的是位置i左边的数的乘积,那么L[0]=1,因为第一个数字左边没数那么为了不影响乘积初始值就设置为1,那么L[1]=L[0]*nums[0],那么L[i]=L[i-1

英语每日一段 195

Promising economic indicators won’t instantly reverse the lingering impact of hard times for millions of families, workplace culture expert Jessica Kriegel said. “Perception and reality are sometimes