传送门:点击打开链接
题意:部分背包
思路:最简单的方法当然是二进制分解,不过最近在练习单调队列,用单调队列去做部分背包,也算是单调队列优化dp里迈出的第一步把~
使用范围:只有对于f1(x)=max/min(f2(k))+f3(x) 且f3(x)与k无关,k<x的这种dp,才能用单调队列去维护
对于部分背包这道题,本来的方程应该是dp[r+i*p]=max(dp[r+k*p]+(i-k)*h),k<i,0<=r<=p-1
那么只需要稍微变形一下,就能得到dp[r+i*p]=max(dp[r+k*p]-k*h)+i*h,所以就对应了我之前讲的那个式子了,f2(k)=dp[r+k*p]-k*h,f3(i)=i*h
然后这个维护最大值里面,还有一个限制条件,就是应该有i-k<=c,所以这道题就变成了求一个固定区间长度里的最大值的题目了,那么就能用单调队列来做了
有个地方要注意,就是每次要搞清楚队列什么时候清空,初始条件是什么,以及更新和维护的顺序
#include<map> #include<set> #include<cmath> #include<ctime> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<string> #include<vector> #include<cstring> #include<iostream> #include<algorithm> #include<functional> #define fuck(x) cout<<"["<<x<<"]" #define FIN freopen("input.txt","r",stdin) #define FOUT freopen("output.txt","w+",stdout) using namespace std; typedef long long LL; const int MX = 1e3 + 5; int dp[MX]; int cur, rear; struct Node { int cost, id; Node() {} Node(int _id, int _c) { cost = _c; id = _id; } } Q[MX]; int main() { int T, n, V; //FIN; scanf("%d", &T); while(T--) { memset(dp, 0, sizeof(dp)); scanf("%d%d", &V, &n); for(int i = 1; i <= n; i++) { int p, h, c; scanf("%d%d%d", &p, &h, &c); for(int r = 0; r < p; r++) { cur = rear = 0; Q[rear++] = Node(0, dp[r]); for(int k = 1; k * p + r <= V; k++) { while(cur < rear && k - Q[cur].id > c) cur++; int now = k * p + r, top = Q[cur].cost; Node temp(k , dp[now] - k * h); while(cur < rear && Q[rear - 1].cost < temp.cost) rear--; Q[rear++] = temp; dp[now] = max(dp[now], top + k * h); } } } printf("%d\n", dp[V]); } return 0; }