金明的预算方案
有依赖性的背包正解为树形DP,对本人来说从未学过,但还好,这道题不用,因为每个主件只有两个附件且附件没有自己的附件,那么只需要考虑五种情况 :
一. 只买主件
二. 买主件和第一个附件
三. 买主件和第二个附件
四. 买主件和两个附件
五. 什么都不买
那么这道题就十分简单了。
不过,该怎么确定主件的附件呢?
首先要定义一个结构体数组,用来存放价格,重要度,是否为附件,编号,附件个数,附件编号
struct things{
int v,w,q,num;//记录价格,重要度,是否为附件,编号
int s;//记录该零件的附件个数
int k[70];//记录该零件附件的编号
}a[70];
q表示附件的主件的编号,然后创立主件的附件集合(就是存附件的编号)
创建完过后,会发现有些主件没有附件,需要去判定是否有附件,不妨将没有附件的主见的附件认为是零,这样就更加简便,更好理解
for(int i = 1 ; i <= m ; i ++ )//初始化
{
cin>> a[i].v >> a[i].w >> a[i].q ;
a[i].w *= a[i].v ;
a[i].num = i;//记录编号
if(a[i].q != 0)
{
a[ a[i].q ].s ++;//附件个数++
a[ a[i].q ].k [a[a[i].q ].s] = a[i].num ;//找到主件,记录附件编号
}//为主件建立附件集合
}
这样就创立了关于主件的附件集合,有了它之后,这道题就变成了一道简单的0.1背包问题,在DP时加一个过滤附件的判定,这道题就愉快的解决了。需要注意的是,在选择的时候,一定要记得比较一下是否有足够的钱去购买附件和主件,不然容易数组越界!!!(血的教训)
#include<iostream>
using namespace std;
int n , m , dp[50000] ;
struct things{
int v , w , q , num ;//记录价格,重要度,是否为附件,编号
int s ;//记录该零件的附件个数
int k[70] ;//记录该零件附件的编号
}a[70];
int max(int x , int y )
{
if(x > y) return x ;
return y ;
}
int main()
{
cin>> n >> m ;
for(int i = 1 ; i <= m ; i ++ )//初始化
{
cin>> a[i].v >> a[i].w >> a[i].q ;
a[i].w *= a[i].v ;
a[i].num = i;//记录编号
if(a[i].q != 0)
{
a[ a[i].q ].s ++;//附件个数++
a[ a[i].q ].k [a[a[i].q ].s] = a[i].num ;//找到主件,记录附件编号
}//为主件建立附件集合
}
for(int i = 1 ;i <= m ; i ++ )//dp
{
for(int j = n ; j >= a[i].v ; j -- )
{
if( a[i].q == 0 )//判定是否为主件
{
if( j >= a[i].v )//单独买主件
dp[j]=max(dp[j],dp[j-a[i].v ] + a[i].w);
if(j >= a[ a[i].k[1] ].v + a[i].v )//买主件和第一个附件
dp[j]=max(dp[j],dp[j-a[a[i].k [1]].v -a[i].v ] + a[a[i].k [1]].w + a[i].w );
if(j >= a[ a[i].k[2] ].v + a[i].v )//买主件和第二个附件
dp[j]=max(dp[j],dp[j-a[a[i].k [2]].v -a[i].v ] + a[a[i].k [2]].w + a[i].w );
if(j >= a[a[i].k[1]].v + a[a[i].k [2]].v + a[i].v )//买主件和两个附件
dp[j]=max( dp[j] , dp[j-a[a[i].k [1]].v -a[a[i].k [2]].v -a[i].v ] + a[a[i].k [1]].w + a[a[i].k [2]].w + a[i].w );
}
}
}
cout<< dp[n] ;
}