【牛牛的跳跳棋】dp解法
题目大意
当棋子位于第 i 个格子时,它的下一步可以移动到[𝑖 − 𝑝𝑖, 𝑖 + 𝑝𝑖]范围内的任意一个格子,每次可以使得一个格子的弹力系数𝑝𝑖 + 1,请输出最小操作次数,以及操作序列
分析
考场上的时候,第一眼看到这道题就想到了dp,并很快定义出了状态——dp[i]表示到达第i格所需的最少操作次数,那么对于每个dp[i],它都可以更新[𝑖 − 𝑝𝑖, 𝑖 + 𝑝𝑖]范围内的dp,进一步思考,我们可以忽略掉往回走的情况,因为对于任意i,若能到达该点,那么它前面的元素也一定被走到过
综上:用dp[i]更新dp[i+1]到dp[i+a[i]]的值,除此之外,还要考虑对p[i]进行更改的操作,所以要用dp[i]+1更新dp[i+a[i]+1]的值
用状态转移方程打出dp后,这道题就已经解决了一半,现在需要找出更改序列,而要知道哪些p[i]被更改了,则要先求到从起点到达n+1点的路径,如果从当前点到下一个点的位移为当前p[i]+1的话,证明该点被修改
借鉴当时学lis时输出序列的方式,定义一个数组pre,用来保存该点的前驱,最后通过n+1一步一步检索即可
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 5;
template<typename item>//方便
void read(item &x) {
x = 0;
int f = 1;
char c = getchar();
while(c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
x = (x << 1) + (x << 3) + (c ^ 48);
c = getchar();
}
x *= f;
}
int n , a[MAXN] , dp[MAXN] , pre[MAXN] , ans[MAXN] , cnt;
int main() {
read(n);
for (int i = 1 ; i <= n ; i ++) {
read(a[i]);//输入
}
memset(dp , 0x3f , sizeof(dp));//初始成极大值
dp[1] = 0;//起点在1
for (int i = 1 ; i <= n ; i ++) {
for (int j = i + 1 ; j <= min(n + 1 , i + a[i]) ; j ++) {//用当前i更新其能到达的点,因为终点在 n+1,所以只需枚举到 min(n + 1 , i + a[i])即可
if (dp[j] > dp[i]) {//更新最小值
dp[j] = dp[i];
pre[j] = i;//保存前驱,用以求出序列
}
}
if (dp[i + a[i] + 1] > dp[i] + 1) {//考虑当前点被施法了的情况,所以能走到 i+a[i]+1这个点
dp[i + a[i] + 1] = dp[i] + 1;
pre[i + a[i] + 1] = i;//同样,记录前驱
}
}
printf("%d\n", dp[n + 1]);//先输出最少操作几次
int x = n + 1;//以终点为开始往回搜路径
while(pre[x]) {
if (a[pre[x]] + pre[x] + 1 == x) {//若这次移动的路程为当前弹力系数 +1的话,证明该点被更改过
ans[++ cnt] = pre[x];
}
x = pre[x];//继续往前找,直至前驱为零
}
for (int i = cnt ; i >= 1 ; i --) {
printf("%d ", ans[i]);//输出
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】