背包DP
Posted ShadowAA
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了背包DP相关的知识,希望对你有一定的参考价值。
背包DP
二进制分组优化
考虑优化。我们仍考虑把多重背包转化成 0-1 背包模型来求解。
预处理物品数量是2的次方。且要覆盖物品数量的点。即2 n次方+1到k
index = 0;
for (int i = 1; i <= m; i++)
int c = 1, p, h, k;
cin >> p >> h >> k;
while (k > c)
k -= c;
list[++index].w = c * p;
list[index].v = c * h;
c *= 2;
list[++index].w = p * k;
list[index].v = h * k;
单调队列优化背包
求区间为m中的最小值
t=1;
for (i=1;i<=n;i++)
if ((b[t]<i-m+1)and(t<=w))
t++;
while ((t<=w)and(a[b[w]]>=a[i]))
w--;
w++;
b[w]=i;
if (i>=m)
cout<<a[b[t]]<<\' \';
单调队列求连续子序列最大和
子序列最大长度为m
for (i=1;i<=n;i++)
cin>>x;
a[i]=a[i-1]+x;
ma=a[1];
for (i=1;i<=n;i++)
if ((b[t]<i-m+1)and(t<=w))
t++;
ma=max(ma,a[i]-a[b[t]]);
while ((t<=w)and(a[b[w]]>=a[i]))
w--;
w++;
b[w]=i;
cout<<ma<<\'\\n\';
单调队列优化多重背包
cin>>n>>m;
for (i=1;i<=n;i++)
cin>>v>>z>>k; //价值 重量 数量
for (j=0;j<=z-1;j++)
t=0;w=0;
o=(m-j)/z;
for (l=0;l<=o;l++)
while ((t<w)and(f[j+l*z]-l*v>=c[w-1]))
w--;
b[w]=l;
c[w]=f[j+l*z]-l*v;
w++;
while ((t<w)and(b[t]<l-k))
t++;
f[j+l*z]=max(f[j+l*z],c[t]+l*v);
cout<<f[m];
有依赖的背包
金明的预算方案 洛谷1064
#include<bits/stdc++.h>
using namespace std;
int n,m,i,x,q,t,k,j,l,z,vv,pp,f[400005],v[40005],p[40005],a[40005];
vector<int>b[40005];
int main()
cin>>m>>n;
for (i=1;i<=n;i++)
cin>>v[i]>>p[i]>>x;
p[i]=p[i]*v[i];
if (x==0)
a[++k]=i;
else
b[x].push_back(i);
for (i=1;i<=k;i++)
q=a[i];
t=b[q].size();
for (l=m;l>=v[q];l--)
for (j=0;j<=(1<<t)-1;j++)
vv=v[q];pp=p[q];
for (z=0;z<b[q].size();z++)
if ((1<<z)&j)
vv=vv+v[b[q][z]];
pp=pp+p[b[q][z]];
if (l>=vv)
f[l]=max(f[l],f[l-vv]+pp);
cout<<f[m]<<\'\\n\';
01背包的第K优解
memset(dp, 0, sizeof(dp));
int i, j, p, x, y, z;
scanf("%d%d%d", &n, &m, &K);
for (i = 0; i < n; i++) scanf("%d", &w[i]);
for (i = 0; i < n; i++) scanf("%d", &c[i]);
for (i = 0; i < n; i++)
for (j = m; j >= c[i]; j--)
for (p = 1; p <= K; p++)
a[p] = dp[j - c[i]][p] + w[i];
b[p] = dp[j][p];
a[p] = b[p] = -1;
x = y = z = 1;
while (z <= K && (a[x] != -1 || b[y] != -1))
if (a[x] > b[y])
dp[j][z] = a[x++];
else
dp[j][z] = b[y++];
if (dp[j][z] != dp[j][z - 1]) z++; //策略不同但权值相同的看作重复的就加这句
printf("%d\\n", dp[m][K]);
多人背包 洛谷1858
#include<bits/stdc++.h>
using namespace std;
int k,n,m,i,j,p,x,y,s,t,w,z,f[5005][55],a[55],b[55];
int main()
cin>>k>>m>>n;
for (i=0;i<=m;i++)
for (j=1;j<=k;j++)
f[i][j]=-1000000000;
f[0][1]=0;
for (i=1;i<=n;i++)
cin>>x>>y;
for (j=m;j>=x;j--)
for (p=1;p<=k;p++)
a[p]=f[j-x][p]+y;
b[p]=f[j][p];
t=w=z=1;
while (((t<=k)or(w<=k))and(z<=k))
if (a[t]>b[w])
f[j][z]=a[t++];
else f[j][z]=b[w++];
z++;
for (i=1;i<=k;i++)
s+=f[m][i];
cout<<s<<\'\\n\';
嘻嘻
01背包与完全背包(dp复习)
01背包和完全背包都是dp入门的经典,我的dp学的十分的水,借此更新博客的机会回顾一下
01背包:给定总容量为maxv的背包,有n件物品,第i件物品的的体积为w[i],价值为v[i],问如何选取才能是背包内的物品价值总和最大。
stdin:
5
1 2 3 4 5
5 4 3 2 1
stdout:
14
设dp[i][j]为取前i件物品时容量为j的最优解。
状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
压缩后:dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
for (int i = 1; i <= 4; i++) { for (int j = 1; j <= bagV; j++) { if (j < w[i]) dp[i][j] = dp[i - 1][j]; else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]); } }//二维dp for (int i = 1; i <= 4; i++) { for (int j = maxv; j >= w[i]; j--) { dp[j] = max(dp[j], dp[j - w[i]] + v[i]); } }//一维
完全背包:在01背包的基础上,每件物品都不限次数。
从一维数组上区别0-1背包和完全背包差别就在循环顺序上,0-1背包必须逆序,因为这样保证了不会重复选择已经选择的物品,而完全背包是顺序,顺序会覆盖以前的状态,所以存在选择多次的情况,也符合完全背包的题意。状态转移方程都为dp[j] = max(dp[j],dp[j-w[i]]+v[i])。
for(int i=1; i<=n; i++) for(int j=w[i]; j<=V; j++) f[j]=max(f[j],f[j-w[i]]+c[i]);
以上是关于背包DP的主要内容,如果未能解决你的问题,请参考以下文章