RQNOJ链接:金明的预算方案
此题是动态规划题,属于分组背包问题。
如果一套物品,主件的编号为1,两个附件的编号分别为2,3,因为附件不能单独选择,所以这个问题不是简单的01背包问题。如果把一个主件和附件的集合看做一个分组,每次只能选择分组中的一个物品。就可以把主件1作为一个物品,1+2作为一个物品,1+3作为一个物品,1+2+3作为一个物品,这样就把4种决策看做了4个物品,也可看作4个包裹,决策的时候选择最优的物品即可
f[i]表示i的背包大小可以获得的最大价值。二维数组master[j][k]表示主件编号为j的k号包裹的花费和权重。s_num数组表示master有几个包裹(有几种决策组合)。
由于这道题说物品的价格都是10元的整数倍,所以所有花费都除以10,输出的时候再乘10。
动态规划方程(格式不太对,能表示意思就行):
f[i] = max{f[i],f[j-cost[i][k]]+weight[i][k]} (0<i<m,n>j>=0,0<=k<s_num[i]};
注意一个细节,在动态规划转移时,一定要注意 (j - cost[i][k])是可以等于0的,也就是说,可以只选这一样,而不选其他的。我之前就写成不能等于0,结果弄了好久没AC。
/*
* http://www.rqnoj.cn/Problem_6.html
* 题目:金明的预算方案
* AC(教训:以后记得写状态转移方程,并且,分清楚>和>=)
*/
#include <iostream>
#include <string.h>
#include <limits.h>
using namespace std;
typedef struct things
{
int cost;
int weight;
}Thing;
int main()
{
int i,j,k;
int n,m;
cin>>n>>m;
m++;
n = (n/10)+1;
Thing master[60][4]; // 主件 0 主件 1 主+1 2 主+2 3 主+1+2
char s_num[60];
memset(s_num,0,sizeof(s_num));
memset(master,0,sizeof(master));
for(i=1;i<m;i++)
{
int v,p,q;
cin>>v>>p>>q;
/* 主件 */
if(q == 0)
{
master[i][0].cost = v/10;
master[i][0].weight = p*v/10;
s_num[i] = 1;
}
/* 附件 */
else
{
int index = s_num[q];
master[q][index].cost = v/10 + master[q][0].cost;
master[q][index].weight = p*v/10 + master[q][0].weight;
s_num[q]++;
/* 两个附件 */
if(s_num[q] == 3)
{
s_num[q]++;
master[q][3].cost = master[q][1].cost + master[q][2].cost - master[q][0].cost;
master[q][3].weight = master[q][1].weight + master[q][2].weight - master[q][0].weight;
}
}
}
int f[3200];
memset(f,0,sizeof(f));
for(i=1;i<m;i++)
{
if(s_num[i] == 0) continue;
for(j=n-1;j>=0;j--)
{
int max = f[j];
for(k=0;k<s_num[i];k++)
{
if((j-master[i][k].cost) >= 0)
{
int s_w = f[j-master[i][k].cost] + master[i][k].weight;
if(s_w > max) max = s_w;
}
}
f[j] = max;
}
}
cout<<f[n-1]*10;
return 1;
}