各类背包DP

  1 #include <iostream>
  2 #include <cstring>
  3 #include <cmath>
  4 #include <cstdio>
  5 #include <algorithm>
  6 #include <vector>
  7 #include <string>
  8 #include <map>
  9 #include <set>
 10 #include <stack>
 11 #include <queue>
 12 #include <sstream>
 13 #include <iomanip>
 14 using namespace std;
 15 typedef long long LL;
 16 const int INF=0x4fffffff;
 17 const double EXP=1e-5;
 18 const int MS=100005;
 19 const int SIZE=1000005;
 20 
 21 int value[MS],weight[MS],cnt[MS];
 22 int N,W; 
 23 int dp[MS][MS];
 24 //  01背包问题
 25 
 26 void solve1()
 27 {
 28     memset(dp,0,sizeof(dp));
 29     for(int i=0;i<N;i++)
 30     {
 31         for(int j=0;j<=W;j++)
 32             if(j<weight[i])
 33                 dp[i+1][j]=dp[i][j];
 34             else
 35                 dp[i+1][j]=max(dp[i][j],dp[i][j-weight[i]]+value[i]);
 36     }
 37     printf("%d\n",dp[N][W]);
 38 }
 39 
 40 //   最长公共子序列问题
 41 
 42 char str1[MS],str2[MS];
 43 
 44 void solve2()
 45 {
 46     memset(dp,0,sizeof(dp));
 47     int len1=strlen(str1);
 48     int len2=strlen(str2);
 49     for(int i=0;i<len1;i++)
 50         for(int j=0;j<len2;j++)
 51         {
 52             if(str1[i]==str2[j])
 53                 dp[i+1][j+1]=dp[i][j]+1;
 54             else
 55                 dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
 56         }
 57     printf("%d\n",dp[len1][len2]);
 58 }
 59 
 60 //  完全背包问题   每个物品的数量无穷
 61 
 62 void solve3()
 63 {
 64     memset(dp,0,sizeof(dp));
 65     /*
 66     for(int i=0;i<N;i++)
 67         for(int j=0;j<=W;j++)
 68             for(int k=0;k*weight[i]<=W;k++)
 69                 dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*weight[i]]+k*value[i]);
 70     */
 71     //    优化
 72     for(int i=0;i<N;i++)
 73         for(int j=0;j<=W;j++)
 74             if(j<weight[i])
 75                 dp[i+1][j]=dp[i][j];
 76             else
 77                 dp[i+1][j]=max(dp[i][j],dp[i+1][j-weight[i]]+value[i]);
 78     printf("%d\n",dp[N][W]); 
 79 } 
 80 
 81 int DD[2][MS];
 82 //  完全背包滚动数组求解 
 83 void solve4()
 84 {
 85     memset(DD,0,sizeof(DD));
 86     for(int i=0;i<N;i++)
 87         for(int j=0;j<=W;j++)
 88             if(j<weight[i])
 89                 DD[(i+1)&1][j]=DD[i&1][j];
 90             else
 91                 DD[(i+1)&1][j]=max(DD[i&1][j],DD[(i+1)&1][j-weight[i]]+value[i]);
 92     printf("%d\n",DD[N&1][W]);
 93 } 
 94 
 95 
 96 
 97 
 98 //  重复利用数组虽然可以节省内存空间,但使用不好的话容易出现bug
 99 
100 int D[MS];
101 //  01  背包重复利用数组    
102  
103 void solve5()
104 {
105     memset(D,0,sizeof(D));
106     for(int i=0;i<N;i++)
107         for(int j=W;j>=weight[i];j--)    //  注意循环的方向。 
108             D[j]=max(D[j],D[j-weight[i]]+value[i]);
109     printf("%d\n",D[W]);
110 }
111 
112 
113 // 完全背包
114 //  完全背包重复利用数组 
115 void solve6()
116 {
117     for(int i=0;i<N;i++)
118         for(int j=weight[i];j<=W;j++)  //  注意循环的方向
119             D[j]=max(D[j],D[j-weight[i]]+value[i]);
120     printf("%d\n",D[W]);    
121 } 
122 
123 
124 //  我们碰到的01背包问题  dp[i][w]中w比较小。
125 //  如果W很大,使得dp[N][W]数组开不下。
126 //  max(value[i])比较小的话,那么我们可以改变 dp的对象
127 //  我们针对不同的价值计算最小的重量
128 int MAX_N=1000,MAX_V=1000;
129 void solve7()
130 {
131     fill(dp[0],dp[0]+MAX_N*MAX_V+1,INF);//  用for循环一样的
132     dp[0][0]=0;
133     for(int i=0;i<N;i++)
134         for(int j=0;j<=MAX_N*MAX_V;j++)
135             if(j<value[i])
136                 dp[i+1][j]=dp[i][j];
137             else
138                 dp[i+1][j]=min(dp[i][j],dp[i][j-value[i]]+weight[i]);
139     int res=0;
140     for(int i=MAX_N*MAX_V;i>=0;i--)
141         if(dp[N][i]<=W)
142         {
143             res=i;
144             break;
145         }
146     printf("%d\n",res);
147 } 
148 
149 //  多重部分和问题
150 //有N种物体,第i种物体的数量为cnt[i],价值为value[i],
151 // 问是否存在总价值为K的组合 
152 //  dp[i+1][j]  前i个物体能否恰好使总价值为j 
153 int K=100000;
154 void solve8()
155 {
156     memset(dp,0,sizeof(dp));
157     dp[0][0]=1;    
158     for(int i=0;i<N;i++)
159         for(int j=0;j<=K;j++)
160             for(int k=0;k<=cnt[i]&&k*value[i]<=j;k++)
161                 dp[i+1][j]|=dp[i][j-k*value[i]];
162     if(dp[N][K])    
163         printf("YES\n");
164     else
165         printf("NO\n");     
166 } 
167  
168 // 不同的状态方程绝对了不同的时间复杂度
169 //   dp[i+1][j]表示前i个物品组成价值j时第i种物品最多能剩多少
170 //   如果无法组成价值j那么 标记为-1
171 // 重复利用数组
172 
173 void solve9()
174 {
175     memset(D,-1,sizeof(D));
176     D[0]=0;
177     for(int i=0;i<N;i++)
178         for(int j=0;j<=K;j++)
179         {
180             if(D[j]>=0)
181                 D[j]=cnt[i];  
182             else if(j<value[i]||D[j-value[i]]<0)
183                 D[j]=-1;
184             else
185                 D[j]=D[j-value[i]]-1;    
186         }
187     if(D[K]>=0)
188         printf("YES\n");
189     else
190         printf("NO\n");    
191 } 
192 
193 //  最长上升子序列问题
194 //  dp[i]以value[i]结尾的最长上升子序列的长度
195 //  dp[i]=max(1,dp[j]+1(j<i且value[j]<value[i]);
196  
197 void solve10()
198 {
199     int res=0;
200     for(int i=0;i<N;i++)
201     {
202         D[i]=1;//  本身的长度为1
203         for(int j=0;j<i;j++)
204             if(value[j]<value[i])
205                 D[i]=max(D[i],D[j]+1); 
206         res=max(res,D[i]);
207     }
208     printf("%d\n",res);
209 }
210 
211 // dp +贪心 优化 
212 void  solve11()
213 {
214     fill(D,D+N,INF);
215     for(int i=0;i<N;i++)
216         *lower_bound(D,D+N,value[i])=value[i];
217     printf("%d\n",lower_bound(D,D+N,INF)-D);
218 }
219 
220 //  超大重量和价值的01背包问题
221 //  折半枚举+二分查找
222 
223 struct node
224 {
225     LL w,v;
226     bool operator<(const node &a)const
227     {
228         return (w<a.w)||(w==a.w&&v<a.v);
229     }
230 }nodes[MS];
231 
232 LL find(LL w,int cnt)
233 {
234     int l=0,r=cnt;
235     while(r-l>1)
236     {
237         int mid=(l+r)/2;
238         if(nodes[mid].w<=w)
239             l=mid;
240         else
241             r=mid;
242     }    
243     return nodes[l].v;
244 } 
245 
246 void solve12()
247 {
248     int n1=N/2;
249     int cnt=0;
250     for(int i=0;i<(1<<n1);i++)
251     {
252         LL sw=0,sv=0;
253         for(int j=0;j<n1;j++)
254         {
255             if((i>>j)&1)
256             {
257                 sw+=weight[j];
258                 sv+=value[j];
259             }
260         }
261         nodes[cnt].w=sw;
262         nodes[cnt++].v=sv;
263     }
264     sort(nodes,nodes+cnt);
265     int last=0;
266     for(int i=0;i<cnt;i++)
267     {
268         if(nodes[last].v<nodes[i].v)
269             nodes[++last]=nodes[i];
270     }
271     cnt=last+1;
272     LL ans=0;
273     for(int i=0;i<(1<<(N-n1));i++)
274     {
275         LL sw=0,sv=0;
276         for(int j=0;j<(N-n1);j++)
277         {
278             if((i>>j)&1)
279             {
280                 sw+=weight[n1+j];
281                 sv+=value[n1+j];
282             }
283         }
284         if(sw<=W)
285         {
286             LL tv=find(W-sw,cnt);
287             ans=max(ans,sv+tv);
288         }
289     }
290     printf("%lld\n",ans);
291 }
292 
293 //  有两种超大重量,超大价值的物体,数量无线。
294 //   在重量不超过W的情况下,能得到的最大价值
295 //  贪心+枚举
296 
297 void solve13()
298 {
299     LL W,w1,w2,v1,v2;
300     scanf("%lld%lld%lld%lld%lld",&W,&v1,&v2,&w1,&w2);
301     if(w1>w2)
302     {
303         swap(w1,w2);   //  交换 
304         swap(v1,v2);    
305     }    
306     if(w2>SIZE)    //  这里SIZE根据题目设置为1000
307     {
308         LL ans=0;
309         for(LL s=0;s*w1<=W;s++)
310         {
311             LL t=(W-s*w1)/w2;
312             LL cur=s*v1+t*v2;
313             if(cur>ans)
314                 ans=cur;
315         }
316         printf("%lld\n",ans);
317         return ;
318     } 
319     LL ans=0;
320     LL big_v=max(w1*v2,w2*v1);
321     for(LL s=0;s<w2;s++)
322     {
323         for(LL t=0;t<w1;t++)
324         {
325             LL w=s*w1+t*w2;
326             LL big=(W-w)/(w1*w2);
327             LL cur=s*v1+t*v2+big*big_v;
328             if(cur>ans)
329                 ans=cur;
330         }
331     }
332     printf("%lld\n",ans);
333     return ;
334 } 
335 
336 void solve14()
337 {
338     int sum=cnt[0]*1+cnt[1]*2+cnt[2]*3+cnt[3]*4+cnt[4]*5+cnt[5]*6;
339     memset(dp,0,sizeof(dp));
340     dp[0]=1;
341     if((sum&1)==0)
342     {
343         for(int i=0;i<6;i++)
344         {
345             int num=cnt[i];
346             if(num==0)
347                 continue;
348             for(int k=1;num>0;k<<=1)
349             {
350                 int mul=min(k,num);
351                 for(int j=sum/2;j>=mul*(i+1);j--)
352                     dp[j]|=dp[j-mul*(i+1)];
353                     //dp[j]=max(dp[j],dp[j-mul*w[i]]+v[i]*mul);
354                 num-=mul;
355             }
356             
357         }
358     }        
359     printf("Collection #%d:\n",kase++); 
360     if((sum&1)==0&&dp[sum/2])
361         printf("Can be divided.\n");
362     else
363         printf("Can't be divided.\n");
364     printf("\n");
365 }

 

posted @ 2015-04-10 19:35  daydaycode  阅读(78)  评论(0编辑  收藏  举报