【题解】胖胖吃糖果

image

简化题意

给定 \(n\) 个正整数,要求从中选出若干个,在满足所选任意两数位置差 \(\le m\) 的前提下所选数之和最小,输出最小和。

DP

显然状态是线性的。

题目对所选数的位置有限制,因此设 \(f[i]\) 为选第 \(i\) 个数的前提下前 \(i\) 个数满足题意的最小和。
初值:\(f[0] = 0\)
转移:\(f[i] = min(f[j]+a[i]),j\in[i-m,i)\)
答案:\(min(f[i]),i\in[n-m+1,n]\)
这样就得到了 \(O(NM)\) 的算法。

Code

// 70pts
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 1;

int n,m,a[N];

int f[N],ans;

int main() {
	ios::sync_with_stdio(false);cin.tie(0);
	cin>>n>>m;
	for(int i = 1; i <= n; ++i) cin>>a[i];
	memset(f, 0x3f, sizeof f);
	f[0] = 0;
	for(int i = 1; i <= n; ++i)
		for(int j = max(0,i-m); j < i; ++j)
			f[i] = min(f[i], f[j]+a[i]);
	ans = 0x3f3f3f3f;
	for(int i = n-m+1; i <= n; ++i) ans = min(ans, f[i]);
	cout<<ans;
	return 0;
}

单调队列优化

因为 \(i\)\(i+1\) 待选决策集合的重叠部分\([i-m+1,i)\),所以如果 \(j<k且f[j]>f[k]\)(即包含 \(j\) 的待选决策集合一定包含 \(k\),且 \(j\)\(k\) 劣),那么 \(f[j]\) 就是无用决策,可以排除。
观察状态转移方程,可以将 \(f[i] = min(f[j]+a[i]),j\in[i-m,i)\) 拆成分别只与 \(i\) 或只与 \(j\) 有关的两部分,即 \(min(f[j]),j\in[i-m,i)\)\(a[i]\),显然是单调队列优化dp的模型。用单增队列维护 \(f[j]\) 即可 \(O(1)\) 转移,总时间复杂度 \(O(N)\)

AC Code

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 1;

int n,m,a[N];

int f[N],ans;
int q[N],fro=1,rea;

int main() {
	ios::sync_with_stdio(false);cin.tie(0);
	cin>>n>>m;
	for(int i = 1; i <= n; ++i) cin>>a[i];
	ans = 0x3f3f3f3f;
	q[++rea] = 0; // 入队 f[0]
	for(int i = 1; i <= n; ++i) {
		while( fro <= rea && q[fro]+m < i ) ++fro; // 排除过时决策
		f[i] = f[q[fro]] + a[i]; // 转移
		while( fro <= rea && f[i] <= f[q[rea]] ) --rea; // 维护单调性
		q[++rea] = i; // 入队 f[i]
		if( i+m-1 >= n ) ans = min(ans, f[i]); // 更新答案
	}
	cout<<ans;
	return 0;
}
posted @ 2021-05-12 21:03  401rk8  阅读(43)  评论(0编辑  收藏  举报