Codeforces28D Don't fear, DravDe is kind DP
Posted itst
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces28D Don't fear, DravDe is kind DP相关的知识,希望对你有一定的参考价值。
题目传送门:http://codeforces.com/problemset/problem/28/D
题意:给你$N$个物品,每个物品有其价格$P_i$,之前必须要买的物品价格和$L_i$,之后必须要买的物品价格和$R_i$和价值$W_i$。试给出一种物品的选择方案,使得满足所有选择的物品的条件且选择物品的价值和最大(物品的选择顺序必须要与原来的顺序相同)。$N leq 10^5 , P , L , R leq 10^5 , W leq 10^4$
很像背包DP,所以就是背包DP(雾
我们能够发现从物品$i$转移到物品$j$的充要条件是:$R_i=R_j+P_j$且$L_i+P_i=L_j$。将二式相加得$P_i+L_i+R_i=P_j+L_j+R_j$,也就是说$P+L+R$相等的物品才能够互相转移。所以我们可以考虑使用$vector$存每个$P+L+R$对应的物品,对于每个组跑一次DP。因为每一个物品的转移只能从$L$到$L+P$,所以转移是$O(1)$的,所以DP总复杂度为$O(n)$。注意DP数组的清空推荐使用还原而不是memset,这样还原的复杂度才是$O(n)$。
获得了最大的价值之后,对对应的那一个组别再跑一遍DP,跑出方案。方案的记录可以通过记录某一个物品选择前最后选择的物品来实现。总复杂度为$O(n)$。
注意每一个组别一定要有始有终(也就是选择的物品必须要有$L=0$与$R=0$的物品,也可以通过这一个来剪一些枝降低常数)
关于输出方案其实可以使用递归,但是因为本机会爆栈所以用循环+vector输出
1 #include<bits/stdc++.h> 2 #define MAXN 3000010 3 #define MAXM 200010 4 using namespace std; 5 6 inline int read(){ 7 int a = 0; 8 char c = getchar(); 9 while(!isdigit(c)) 10 c = getchar(); 11 while(isdigit(c)){ 12 a = (a << 3) + (a << 1) + (c ^ ‘0‘); 13 c = getchar(); 14 } 15 return a; 16 } 17 18 struct thing{ 19 int w , p , l , r , ind; 20 }now; 21 vector < thing > v[MAXN]; 22 vector < int > anss; 23 int maxPri[MAXN] , last[MAXM] , k[MAXN] , cnt[MAXN] , ans[MAXN] , maxN = -1 , maxDir; 24 bool haveEnd[MAXN]; 25 26 void out(int dir){ 27 while(dir != -1){ 28 anss.push_back(v[maxDir][dir].ind); 29 dir = last[dir]; 30 } 31 for(int i = anss.size() - 1 ; i >= 0 ; i--) 32 printf("%d " , anss[i]); 33 } 34 35 int main(){ 36 int N = read(); 37 for(int i = 1 ; i <= N ; i++){ 38 now.w = read(); 39 now.p = read(); 40 now.l = read(); 41 now.r = read(); 42 now.ind = i; 43 if(cnt[now.p + now.l + now.r] || now.l == 0){ 44 cnt[now.p + now.l + now.r]++; 45 v[now.p + now.l + now.r].push_back(now); 46 if(now.r == 0) 47 haveEnd[now.p + now.l + now.r] = 1; 48 } 49 } 50 memset(maxPri , -0x3f , sizeof(maxPri)); 51 maxPri[0] = 0; 52 for(int i = 0 ; i <= 3000000 ; i++) 53 if(cnt[i] && haveEnd[i]){ 54 for(int j = 0 ; j < cnt[i] ; j++) 55 maxPri[v[i][j].l + v[i][j].p] = max(maxPri[v[i][j].l + v[i][j].p] , maxPri[v[i][j].l] + v[i][j].w); 56 if(maxN < maxPri[i]){ 57 maxN = maxPri[i]; 58 maxDir = i; 59 } 60 for(int j = 0 ; j < cnt[i] ; j++) 61 maxPri[v[i][j].l + v[i][j].p] = -0x3f3f3f3f; 62 } 63 memset(last , -1 , sizeof(last)); 64 memset(k , -1 , sizeof(k)); 65 for(int j = 0 ; j < cnt[maxDir] ; j++) 66 if(maxPri[v[maxDir][j].l + v[maxDir][j].p] < maxPri[v[maxDir][j].l] + v[maxDir][j].w){ 67 maxPri[v[maxDir][j].l + v[maxDir][j].p] = maxPri[v[maxDir][j].l] + v[maxDir][j].w; 68 last[j] = k[v[maxDir][j].l]; 69 k[v[maxDir][j].l + v[maxDir][j].p] = j; 70 ans[v[maxDir][j].l + v[maxDir][j].p] = ans[v[maxDir][j].l] + 1; 71 } 72 printf("%d " , ans[maxDir]); 73 out(k[maxDir]); 74 return 0; 75 }
以上是关于Codeforces28D Don't fear, DravDe is kind DP的主要内容,如果未能解决你的问题,请参考以下文章
[Codeforces 28D] Do not fear,DravDe is kind