背包问题是一种组合优化的NP完全问题. 问题可以描述为: 给定一组物品, 每种物品都有自己的体积和价值, 在限定的总体积内, 我们如何选择, 才能使得物品的总价值最高.
背包九讲
① 01背包问题
有
N
件物品和一个容量是V
的背包, 每件物品只能使用一次第
i
件物品的体积是vi
, 价值是wi
求解将哪些物品装入背包, 可使这些物品的总体积不超过背包容量, 且总价值最大
② 完全背包问题
有
N
种物品和一个容量是V
的背包, 每种物品都有无限件可用第
i
件物品的体积是vi
, 价值是wi
求解将哪些物品装入背包, 可使这些物品的总体积不超过背包容量, 且总价值最大
③ 多重背包问题
有
N
种物品和一个容量是V
的背包第
i
种物品最多有si
件, 每件体积是vi
, 价值是wi
求解将哪些物品装入背包, 可使物品体积总和不超过背包容量, 且总价值最大
④ 混合背包问题
有
N
种物品和一个容量是V
的背包物品一共有三类:
第一类物品只能用一次 (01背包)
第二类物品可用无限次 (完全背包)
第三类物品最多只能用
si
次 (多重背包)第
i
种物品, 每件体积是vi
, 价值是wi
求解将哪些物品装入背包, 可使物品体积总和不超过背包容量, 且价值总和最大
⑤ 二位费用的背包问题
有
N
件物品和一个容量是V
的背包, 背包能承受的最大重量是M
每件物品只能用一次, 体积是
vi
, 重量是mi
, 价值是wi
求解将哪些物品装入背包, 可使物品总体积不超过背包容量, 总重量不超过背包可承受的最大重量, 且价值总和最大
⑥ 分组背包问题
有
N
组物品和一个容量是V
的背包每组物品有若干个, 同一组内的物品最多只能选一个
每件物品的体积是
vij
,价值是wij
,其中i
是组号,j
是组内编号求解将哪些物品装入背包, 可使物品总体积不超过背包容量, 且总价值最大
⑦ 有依赖的背包问题(树上背包问题)
有
N
件物品和一个容量是V
的背包物品之间具有依赖关系, 且依赖关系组成一棵树的性质. 如果选择一个物品, 则必须选择它的父节点
每件物品的编号是
i
, 体积是vi
, 价值是wi
, 依赖的父节点编号是pi
.物品的下标范围是 1 ~ N求解将哪些物品装入背包, 可使物品总体积不超过背包容量, 且总价值最大
⑧ 背包问题求方案数
有
N
件物品和一个容量是V
的背包, 每件物品只能用一次第
i
件物品的体积是vi
, 价值是wi
求解将哪些物品装入背包, 可使这些物品的总体积不超过背包容量, 且总价值最大
输出最优选法的方案数
⑨ 背包问题求具体方案
有
N
件物品和一个容量是V
的背包, 每件物品只能用一次第
i
件物品的体积是vi
, 价值是wi
求解将哪些物品装入背包, 可使这些物品的总体积不超过背包容量, 且总价值最大
输出字典序最小的方案, 这里的字典序是指所选物品的编号所构成的序列, 物品的编号范围是 1 ~ N
01背包问题
//朴素版解法
int dp[N][N]; //dp[i][j]表示前i个物品,背包容量是j的情况下的最大价值
int w[N],v[N];
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
dp[i][j]=dp[i-1][j];
if(j>=v[i])dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
}
//滚动数组优化
int dp[N];
int v[N],w[N];
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
完全背包问题
//朴素版未优化
int dp[N][N];
int v[N],w[N];
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k*v[i]<=j;k++)
dp[i][j]==max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
//朴素解法
int dp[N][N];
int v[N],w[N];
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
dp[i][j]=dp[i-1][j];
if(v[i]<=j)dp[i][j]=max(dp[i][j],dp[i][j-v[i]]+w[i]);
}
//优化空间版解法
int dp[N];
int v[N],w[N];
for(int i=1;i<=n;i++)
for(int j=v[i];j<=m;j++)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
多重背包问题
//朴素版解法
int dp[N][N];
int v[N],w[N],s[N];
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=s[i]&&k*v[i]<=j;k++)
dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
二进制优化: 将 s[i] 个物品拆分成 \(log_2\) s[i] 份, 作01背包问题
//二进制优化
int dp[N];
int v[N],w[N];
int cnt=0;
for(int i=1;i<=n;i++)
{
int a,b,s;
cin>>a>>b>>s;
int k=1;
while(k<=s)
{
cnt++;
v[cnt]=a*k;
w[cnt]=b*k;
s-=k;
k*=2;
}
if(s>0)
{
cnt++;
v[cnt]=a*s;
w[cnt]=b*s;
}
}
n=cnt;
for(int i=1;i<=n;i++)
for(int j=m;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
混合背包问题
int n,m; //si=-1表示第i种物品只能用1次
int v[N],w[N],s[N]; //si=0表示第i种物品可以用无限次
int dp[N]; //si>0表示第i种物品可以用si次
struct Good
{
int kind;
int v,w;
};
vector <Good> good;
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)cin>>v[i]>>w[i]>>s[i];
for(int i=0;i<n;i++)
{
if(s[i]==-1)good.push_back({-1,v[i],w[i]});
else if(s[i]==0)good.push_back({0,v[i],w[i]});
else //多重背包二进制优化(分组)后,对于每一组都相当于是01背包
{
for(int k=1;k<=s[i];k*=2)
{
s[i]-=k;
good.push_back({-1,v[i]*k,w[i]*k});
}
if(s[i]>0)good.push_back({-1,v[i]*s[i],w[i]*s[i]});
}
}
//对于01背包和完全背包分两种情况状态转移即可
for(auto goods:good)
{
//01背包
if(goods.kind==-1)
{
for(int j=m;j>=goods.v;j--)
dp[j]=max(dp[j],dp[j-goods.v]+goods.w);
}
//完全背包
else
{
for(int j=goods.v;j<=m;j++)
dp[j]=max(dp[j],dp[j-goods.v]+goods.w);
}
}
cout<<dp[m]<<endl;
return 0;
}
二位费用的背包问题
//dp[i][j]表示总体积是i,总重量是j的情况下,最大价值是多少
//同时对于状态转移的时候,两层for循环,一层是体积限制,一层是重量限制
int a,b,c; //a为物品数量,b为背包最大容量,c为背包最大重量
int v[N],t[N],w[N];
int dp[N][N];
for(int i=0;i<a;i++)
for(j=b;j>=v[i];j--)
for(int k=c;k>=t[i];k--)
dp[j][k]=max(dp[j][k],dp[j-v[i]][k-t[i]]+w[i]);
分组背包问题
int dp[N];
int v[N][N],w[N][N],s[N];
for(int i=1;i<=n;i++)
for(int j=m;j>=0;j--)
for(int k=0;k<s[i];k++)
if(v[i][k]<=j)dp[j]=max(dp[j],dp[j-v[i][k]]+w[i][k]);
树上背包问题
特点:
N
个物品,V
容量的背包, 物品之间有依赖关系, 且依赖关系可以构成一棵树, 如果选择某一个物品, 那么就必须选择这个物品的父亲物品
父节点编号范围:
内部节点: 1 \(\leqslant\) \(p_i\) \(\leqslant\) N
根节点 \(p_i\) = -1
dp[i][j]
表示选择节点i
(在这里也就是根节点), 总体积是j
时, 最大价值是多少. 当我们选择根节点后, 对于根节点的每一棵子树, 都相当于对根节点做分组背包了, 每一个根节点相当于一个组, 对于组内的节点分为选择或者不选择
int n,m;
int v[N],w[N];
int dp[N][N];
int h[N],e[N],ne[N],idx;
void add (int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs (int u)
{
for(int i=h[u];i!=-1;i=ne[i])
{
int son=e[i];
dfs(son);
for(int j=m-v[u];j>=0;j--)
for(int k=0;k<=j;k++)
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[son][k]);
}
//将根节点u加上
for(int i=m;i>=v[u];i--)
dp[u][i]=dp[u][i-v[u]]+w[u];
//如果选择了子节点后,还没有选择到最后的root,那么就不能选择当前节点及其根节点了
for(int i=0;i<v[u];i++)
dp[u][i]=0;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
int root;
for(int i=1;i<=n;i++)
{
int p;
cin>>v[i]>>w[i]>>p;
if(p==-1)root=i;
else add(p,i);
}
dfs(root);
cout<<dp[root][m]<<endl;
return 0;
}
背包问题求方案数
特点: 在背包问题下, 求出最优选法的方案数
f[j]
表示体积恰好是j
的情况下, 最大价值是多少
g[j]
表示体积恰好是j
的情况下, 方案数是多少这个地方的
f[]
数组的含义与前面的背包问题不一样, 所有在这里的初始化, 也与前面不同
f[0 - N]
= - \(\infty\) ,f[0]
= 0 ,g[0]
= 1
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
int n,m;
int v[N],w[N];
int f[N],g[N];
int main()
{
cin>>n>>m;
g[0]=1;
for(int i=1;i<=m;i++)f[i]=-INF;
for(int i=0;i<n;i++)cin>>v[i]>>w[i];
for(int i=0;i<n;i++)
for(int j=m;j>=v[i];j--)
{
int t=max(f[j],f[j-v[i]]+w[i]);
int s=0;
if(t==f[j])s+=g[j];
if(t==f[j=v[i]]+w[i])s+=g[j-v[i]];
if(s>=mod)s-=mod;
f[j]=t;
g[j]=s;
}
int maxx=0;
for(int i=0;i<=m;i++)maxx=max(maxx,f[i]);
int ans=0;
for(int i=0;i<=m;i++)
if(maxx==f[i])
{
ans+=g[i];
if(ans>=mod)ans-=mod;
}
cout<<ans<<endl;
return 0;
}
背包问题求具体方案
特点: 在普通背包的基础下, 输出选择方案
我们要输出所选的方案, 首先我们按照普通背包的问题求解后, 我们会得到
dp[]
数组, 因此如果我们要求出选择了哪一些物品, 我们就可以对dp[]
数组进行反推如果
dp[i][j]
=dp[i-1][j]
, 说明没有选择第i
个物品如果
dp[i][j]
=dp[i-1][j-v[i]]
+w[i]
, 说明选择了第i
个物品这个题目为了解唯一, 它的答案方案是字典序最小的方案, 因此我们可以按照贪心的思想去做, 如果能选择第
i
个物品, 那么我们就直接选择第i
个物品, 而不选第i+1
个物品(如果选择第i
个物品和第i+1
个物品所得的最大价值相同的话)所以正确思路是我们按
n
\(\to\)1
的顺序求解01背包, 接着按1
\(\to\)n
的顺序反推每个物品是否被选
int n,m;
int v[N],w[N];
int dp[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=n;i>=1;i--)
for(int j=0;j<=m;j++)
{
dp[i][j]=dp[i+1][j];
if(j>=v[i])dp[i][j]=max(dp[i][j],dp[i+1][j-v[i]]+w[i]);
}
int vol=m;
for(int i=1;i<=n;i++)
if(vol-v[i]>=0&&dp[i][vol]==dp[i+1][vol-v[i]]+w[i])
{
cout<<i<<' ';
vol-=v[i];
}
return 0;
}
标签:背包,int,问题,件物品,体积,物品,dp From: https://www.cnblogs.com/evilboy/p/17364626.html