依赖背包
问题来源:P1064 金明的预算方案(NOIP2006)
背包中的物品存在依赖关系,也就是说,如果选i物品,就必须选择j物品,j为主件,i为附件
分析
按照背包的一般思路,我们可以考虑对所有的主件进行dp选择,然后在选择每一个主件的时候再枚举附件,当然这个题只是每个主件最多只有2个附件,所以这就只有5个策略,但是如果我们推广到n呢,策略则有2n+1之多,为指数级。
我们在上边的基础上,我们可以做一些优化:
将每一个主件和他的附件组成一个物品组,如果我们将每个物品组的对应的不同背包的最优解求出来,然后在对所有的物品组进行求背包问题,答案就求出来了。
那么我们怎么求每个物品组的对应的不同背包的最优解,显而易见,这也是一个背包问题,但是这个背包中如果有物品则至少有这个物品组的主件
一般化:这个题附件不能再有附件,但是如果附件再有附件呢?显然我们同样也可以创建个2级物品组 3级物品组……,对每个附件集进行01背包求解,逐层推之;
代码实现
代码一:这是枚举的代码,对每个主件的附件进行枚举。下边的代码用的记忆化搜索,其实完全可以改成动态规划求解QWQ(因为每个附件集最多2个附件,所以最多有5种策略,完全可以用5个if语句求解)
另外对于这个题来说,因为题目已有了限定,显然枚举更加快速简便,(上述的算法只能用于多附件问题)
#include<iostream>
#include<vector>
using namespace std;
vector<int>s[61];//存储子节点
int v[61];//价格
int r[61];//等级
int f[100];//根节点
int n,t;
int ans=0;
int dp[61][1000];
int dfs(int i,int t)
{
if(t<=0)
return 0;
if(i==-1)
return 0;
if(dp[i][t]>0)
return dp[i][t];
int sum,w;
int y=0;
if(t-v[f[i]]>=0)
y = dfs(i-1,t-v[f[i]])+v[f[i]]*r[f[i]];
for(int j=0;j<(int)(s[f[i]].size());j++)
{
sum = v[f[i]],w = v[f[i]]*r[f[i]];
if(sum<=t)
{
for(int h=j;h<(int)s[f[i]].size();h++)
{
int k=s[f[i]][h];
sum+=v[k];
w+=v[k]*r[k];
if(sum<=t)
y = max(y,dfs(i-1,t-sum)+w);
else
break;
}
}
}
y = max(y,dfs(i-1,t));
return dp[i][t]=y;
}
int main()
{
cin>>t>>n;
int q;
for(int i=0;i<n;i++)
{
cin>>v[i]>>r[i]>>q;
if(q==0)
f[ans++]=i;
else
s[q-1].push_back(i);
}
cout<<dfs(ans-1,t);
return 0;
}
代码二:(下边的代码在洛谷上只能过一半QWQ,其他都是超限,但对于多附件的题目来说,下面的算法更加的实用)
#include<iostream>
#include<vector>
using namespace std;
int dp[32001];//主件DP
int dp2[32001];//附件DP2
int v[70];//物品的价值
vector <int>fu[62];
int f[62];
int r[70];
int main()
{
int ans=0;
int n,m;//n钱 ,m个物品
int a,c;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>a>>r[i]>>c;
v[i] = a;
if(c==0)//主件
f[ans++] = i;
else//附件
fu[c].push_back(i);//c的附件有i
}
for(int i=0;i<ans;i++)//对每个主件的附件进行dp
{
//初始化
int k = f[i];//主件号
int w =v[k]*r[k];
if(fu[k].size()>0)
{
for(int t = v[k];t<=n;t++)
dp2[t] = w;
}//在背包足够大的情况下,一定选择主件
for(int j=0;j<fu[k].size();j++)//第j个附件
{
int t = fu[k][j];//附件号
w = v[t]*r[t];
for(int h=n;h>=v[t];h--)//容量(价格)
{
if(h-v[t]>=v[k])//如果减去附件的价格后的价格够主件
dp2[h] = max(dp2[h],dp2[h-v[t]]+w);
}
}
for(int j=n;j>=v[k];j--)
{
if(fu[k].size()>0)
{
for(int h = j;h>=v[k];h--)
dp[j] = max(dp[j],dp[j-h]+dp2[h]);
}
else
dp[j]= max(dp[j],dp[j-v[k]]+v[k]*r[k]);
}
}
cout<<dp[n];
return 0;
}
参考: https://www.luogu.org/discuss/show/47533