CF28D Don't fear, DravDe is kind 背包

题目传送门: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
 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\n" , ans[maxDir]);
73     out(k[maxDir]);
74     return 0;
75 }

 

转载于:https://www.cnblogs.com/Itst/p/9762684.html

你可能感兴趣的:(CF28D Don't fear, DravDe is kind 背包)