各类背包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 }