单调队列优化Dp例题

#875. 选元素(数据加强版)

在这里插入图片描述
思路:
如果题目是,每 k k k 个数中就必须选择一个,选择的总数固定,那么可以写出状态转移方程:
f [ i ] f[i] f[i] i i i 个元素时,选择到的最大值:
f [ i ] = m a x ( f [ t ] ) + a [ i ] f[i] = max(f[t]) + a[i] f[i]=max(f[t])+a[i] 其中 ( i − k < t < i (i - k < t < i (ik<t<i),可以发现 f [ t ] f[t] f[t] 可以用单调队列来维护 m a x ( f [ t ] ) max(f[t]) max(f[t])模板 单调队列优化dp)。
但是该题要求了选择的个数为 x x x f [ i ] [ j ] f[i][j] f[i][j] 表示 第 i i i 个元素,选择了 j j j 个时的最大值。
此时的状态转移方程:
f [ i ] [ j ] = m a x ( f [ t ] [ j − 1 ] ) + a [ i ] f[i][j] = max(f[t][j - 1]) + a[i] f[i][j]=max(f[t][j1])+a[i] 其中 ( i − k < t < i (i - k < t < i (ik<t<i), j j j 只能由 j − 1 j - 1 j1 更新而来,除此以外和上面情况完全相同,可以用单调队列维护 m a x ( f [ t ] [ j − 1 ] ) ) max(f[t][j - 1])) max(f[t][j1])) j j j 的值用一层循环来更新即可。最终答案在最后 k k k 个数中找最大值即可

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> PII;
const int N = 3e3 + 10;
ll s[N], f[N][N], sum;
int n, k, x, q[N];

int main() {
   ios::sync_with_stdio(false); 
   cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
   freopen("D:/Cpp/program/Test.in", "r", stdin);
   freopen("D:/Cpp/program/Test.out", "w", stdout);
#endif
    cin >> n >> k >> x;
    for(int i = 1; i <= n; i ++) {
        cin >> s[i];
        // sum += s[i];
    }
    memset(f, -0x3f, sizeof f);
    f[0][0] = 0;
    for(int j = 1; j <= x; j ++) {
    int hh = 0, tt = -1;
        for(int i = 0; i <= n; i ++) {
            if(hh <= tt && i - q[hh] > k) hh ++;
            if(hh <= tt) f[i][j] = f[q[hh]][j - 1] + s[i];
            // else f[i][j] = max(f[i][j], f[i - 1][j]);
            while(hh <= tt && f[q[tt]][j - 1] <= f[i][j - 1]) tt --;
            q[++ tt] = i;
        }
    }
    ll ans = -1;
    for(int i = n - k + 1; i <= n; i ++) ans = max(ans, f[i][x]);
    cout << ans << '\n';
}
// 1 1 1 1 1 1 1

C. Jump and Treasure

在这里插入图片描述
在这里插入图片描述
题目大意, n n n 个元素, q q q 询问,每次最多往右跳 p p p 步,最开始在 0 0 0 的位置,每次询问如果每次跳 x x x 的倍数步,可以获得元素和的最大值是多少。
f [ i ] f[i] f[i] 为跳到 第 i i i 个元素时获得的最大值, 那么 f [ i ] = m a x ( f [ t ] ) + w [ i ] f[i] = max(f[t]) + w[i] f[i]=max(f[t])+w[i] 其中 ( i − p < t < i ) (i - p < t < i) (ip<t<i),也可也用单调队列来维护 f [ i ] f[i] f[i] 的值,本题加了一个跳 x x x 的倍数步子的限制,对于每次询问,可以预先离散化处理出能够到达哪些点上,再用单调队列维护即可。因为存在多个 x x x 有可能相同,所以为了减少冗余操作,可以将询问先存下来再来计算;记得开 l o n g l o n g long long longlong

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
ll qu[N], ans[N];
vector<int> query[N];

int main() {
   ios::sync_with_stdio(false); 
   cin.tie(0); cout.tie(0);
#ifndef ONLINE_JUDGE
   freopen("D:/Cpp/program/Test.in", "r", stdin);
   freopen("D:/Cpp/program/Test.out", "w", stdout);
#endif
    int n, q, p; cin >> n >> q >> p;
    vector<ll> a(n + p + 10, 0);
    for(int i = 1; i <= n; i ++) cin >> a[i];
    for(int i = 1; i <= q; i ++) {
        int x; cin >> x;
        if(x > p) ans[i] = -1e18;
        else query[x].push_back(i);
    }

    for(int i = 1; i <= n; i ++) {
        if(query[i].empty()) continue;
        vector<int> jump;
        jump.push_back(0);
        for(int j = i; j <= n; j += i) jump.push_back(j);
        jump.push_back(n + 1);
        vector<ll> f(jump.size() + 10, -1e18);
        f[0] = 0, qu[0] = 0;
        int hh = 0, tt = 0;
        for(int j = 1; j < jump.size(); j ++) {
            while(hh <= tt && jump[j] - jump[qu[hh]] > p) hh ++;
            f[j] = max(f[j], f[qu[hh]] + a[jump[j]]);
            while(hh <= tt && f[j] >= f[qu[tt]]) tt --;
            qu[++ tt] = j;
        }

        for(auto id : query[i]) ans[id] = f[jump.size() - 1];
    }
    for(int i = 1; i <= q; i ++)  {
        if(ans[i] == -1e18) cout << "Noob" << '\n';
        else cout << ans[i] << '\n';
    }
}
// 1 1 1 1 1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值