首页 > 其他分享 >重学单调队列优化dp

重学单调队列优化dp

时间:2023-02-07 23:00:10浏览次数:60  
标签:5005 队列 ll int dp 单调

再谈单调队列优化dp。

题目:CF1077F1&2 Pictures with Kittens(Easy & hard version)

从n个数中选出m个数,连续k个数至少选出一个,最大化选出数和。

easy version普通dp,hard则需要单调队列优化。

借此机会我也深入体会了一下dp逻辑。

与此题很相似的有一道USACO的题,也是单调队列优化dp,是从n个数中选数,不能选超过连续k个数,最大化选数和(状态 \(f_{i,0|1}\) 表示第 \(i\) 个选或不选)。

设 \(f_{i,j}\) 为前 \(i\) 个数中选 \(j\) 个数,第 \(i\) 个数必选的最大和。

方程:\(f_{i,j} = \max\limits^{i-1}_{p=\max(0,i-k)}f_{p,j-1} + a_i\)

这里有个细节,如果 \(i<k\) 为什么 \(p\) 要从 \(0\) 开始?参考关于dp方程的一个小问题

也可以从 \(1\) 开始。不过要把 \(f_{i,1}\ i<=k\) 赋为 \(a_i\)。我们把 \(f_{0,0}\) 理解为不选,\(p=0\) 开始意味着在 \(j = 1\) 的时候要从不选中转移出初始化的数据。从 \(1\) 开始就需要先前赋值。

还有,\(f\) 数组要初始化为 \(-inf\),不知为何。

总结:做dp题总是会有很多思维误区,比如 \(i>k\) 时 \(f_{i,1}\ne a_i\) 等。平日练习一定要理解透彻,深入总结。同时,dp过程如果不清晰的话,可以画个DAG理解。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a[5005];
int n, m, k;
ll f[5005][5005];
int main()
{
    ios::sync_with_stdio(false);
    memset(f, 0xcf, sizeof(f));
    cin >> n >> k >> m;
    if (n / k > m) {
        cout << -1 << endl;
        return 0;
    }
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        if(i <= k) f[i][1] = a[i];
    }
    for (int j = 1; j <= m; j++) {
        for (int i = 1; i <= n; i++) {
            if (j > i) continue;
            for (int p = max(1, i - k); p < i; p++) {
                f[i][j] = max(f[i][j], f[p][j - 1] + a[i]);
            }
        }
    }
    ll ans = 0;
    for (int i = n - k + 1; i <= n; i++) {
        ans = max(ans, f[i][m]);
    }
    cout << ans << endl;
    return 0;
}

下一步是F2,考虑优化求最大值,一种方法是ST表,一种方法是单调队列。

这道题ST表理论上可以过,但是咕咕了。

下面是单调队列的。

单调队列维护 \(f_{i,j-1}, 1 \le i \le n\) 的max

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a[5005];
int n, m, k;
ll f[5005][5005];
deque<pair<int, ll> >q;
int main()
{
	ios::sync_with_stdio(false);
	memset(f, 0xcf, sizeof(f));
	f[0][0] = 0;
	cin >> n >> k >> m;
	if (n / k > m) {
		cout << -1 << endl;
		return 0;
	}
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		if (i <= k) f[i][1] = a[i];
	}
	for (int j = 1; j <= m; j++) {
		q.clear();
		for (int i = 1; i <= n; i++) {
			while (q.size() && q.front().first + k < i) q.pop_front();
			if (q.size()) f[i][j] = max(f[i][j], q.front().second + a[i]);
			while (q.size() && q.back().second <= f[i][j - 1]) q.pop_back();
			q.push_back(make_pair(i, f[i][j - 1]));
		}
	}
	ll ans = 0;
	for (int i = n - k + 1; i <= n; i++) {
		ans = max(ans, f[i][m]);
	}
	cout << ans << endl;
	return 0;
}

标签:5005,队列,ll,int,dp,单调
From: https://www.cnblogs.com/CYLSY/p/17100097.html

相关文章

  • 【YBT2023寒假Day7 A】出题人(线段树优化DP)
    出题人题目链接:YBT2023寒假Day7A题目大意有一个序列,你要把它分成若干份,每一份的值的和不超过m,而且每一段最大值的和最小。输出每段最大值和的最小值。思路考虑每次......
  • CodeForces - 234 F. Fence DP
    F.Fencetimelimitpertest2secondsmemorylimitpertest256megabytesinputinput.txtoutputoutput.txtVasyashouldpaintafenceinfrontofhisowncottage.The......
  • HDU 5418 Victor and World 状压DP的TSP问题
     VictorandWorldTimeLimit:4000/2000MS(Java/Others)    MemoryLimit:262144/131072K(Java/Others)TotalSubmission(s):1855    AcceptedSubmission......
  • POJ 3311 Hie with the Pie 状态压缩DP TSP问题(两种方法)
    HiewiththePieTimeLimit: 2000MS MemoryLimit: 65536KTotalSubmissions: 9728 Accepted: 5250DescriptionThePizazzPizzeriapridesitselfindeliveringpi......
  • UVA540 Team Queue 队列入门经典
    题意翻译有t个团队的人正在排一个长队。每次新来一个人时,如果他有队友在排队,那么新人会插队到最后一个队友的身后。如果没有任何一个队友排队,则他会被排到长队的队尾。输入......
  • (树形DP+背包)POJ1947Rebuilding Roads
    RebuildingRoadsTimeLimit: 1000MS MemoryLimit: 30000KTotalSubmissions: 13307 Accepted: 6171DescriptionThecowshavereconstructedFarmerJohn'sfarm,w......
  • 树形DP依赖背包 洛谷 P2015 二叉苹果树
    题目描述有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点)这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。我们用一根树枝两端连接的......
  • 树形DP (cf 219D Choosing Capital for Treeland)
    题意翻译题目描述Treeland国有n个城市,这n个城市连成了一颗树,有n-1条道路连接了所有城市。每条道路只能单向通行。现在政府需要决定选择哪个城市为首都。假如城市i成为了首都......
  • 树形DP+概率DP
    动态规划报告树形dp树形DP,即在树上进行的DP。由于树固有的递归性质,树形DP一般都是递归进行的。一般需要在遍历树的同时维护所需的信息以一道题目为例2022CCPC桂林......
  • 消息队列数据丢失及可靠性
    用MQ有个基本原则,就是数据不能多一条,也不能少一条,不能多,就是前面说的重复消费和幂等性问题。不能少,就是说这数据别搞丢了。那这个问题你必须得考虑一下。如果说你这个......