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 }