dp+滑动窗口的应用
设计了一款游戏,需要算出从起点经过若干次跳跃到终点的得分,输入为一个整数数组nums,和一个整数k,
一开始你在下标 0 处。每一步,你最多可以往前跳 k 步,但你不能跳出数组的边界。也就是说,你可以从下标 i 跳到 [i + 1, min(n - 1, i + k)] 包含 两个端点的任意位置。
你的目标是到达数组尾部(下标为 n - 1 ),你的得分为经过的所有数字之和。
#分析:
由题意可以发现可以用动态规划解决,但是显然在dp时会超时,因为你要每次用一次循环查找[i - k, i - 1] 的最大值。因此此时就需要用滑动窗口维护长度为k - 1的窗口中得分的最大值。
d p [ i ] = d p [ q [ h h ] ] + n u m s [ i ] dp[i] = dp[q[hh]] + nums[i] dp[i]=dp[q[hh]]+nums[i]
Sample Input 1
1,-1,-2,4,-7,3
2
Sample Output 1
7
解释:你可以选择子序列 [1,-1,4,3],和为 7 。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int k, len;
int nums[N], dp[N], q[N];
string s;
void run()
{
cin >> s;
for (int i = 0; i < s.size(); i ++)
{
int num;
if (isdigit(s[i]))
{
num = s[i] - '0';
int j;
for (j = i + 1; isdigit(s[j]); j ++)
num = num * 10 + s[j] - '0';
i = j;
nums[len ++] = num;
}
if (s[i] == '-')
{
int j;
num = 0;
for (j = i + 1; isdigit(s[j]); j ++)
num = num * 10 + s[j] - '0';
i = j;
nums[len ++] = -num;
}
}
memcpy(dp, nums, sizeof dp);// 初始化dp数组,得分初始是当前的值
cin >> k;
dp[0] = nums[0];
int hh = 0, tt = 0;
for (int i = 1; i < len; i ++)
{
while (hh <= tt && i - k > q[hh]) hh ++;
dp[i] = dp[q[hh]] + nums[i];
while (hh <= tt && dp[i] >= dp[q[tt]]) tt --;
q[++ tt] = i;
}
cout << dp[len - 1] << endl;
}
int main()
{
ios_base::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
freopen("in.in", "r", stdin);
run();
return 0;
}