问题概述:有一个容量为V的背包和n个物品,第i种物品最多有n[i]件可用,每件体积是w[i],求解将哪些物品装入背
包可使这些物品的价值尽可能接近V但不大于V(POJ1276)
输入样例: 对应输出:
735 735
3 630
4 125 6 5 3 350
633
4
500 30 6 100 1 5 0 1
解题思路:可以直接将这个问题轻松转化为01背包,但复杂度为V*∑n[i]比较高,所以需要优化:
解题过程:对于每个物品,有三种情况:
①:单件该物品体积已经超过背包容量,不可能装的进
②:无法将该物品全部装入背包,直接将它当做完全背包
③:可以将该物品全部装入背包,这种情况比较复杂,可以将这些物品拆成多个单件物品,但复杂度很高,所以需
要二进制优化,假设有30个该物品,每件物品体积为10,将这30个物品拆成5个单件物品即可,它们的体积分别为
10、20、40、80、150,这样对于任意的m(m<=30)件物品都可以用这5个物品表示出来
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int dp[100005], V;
void Alone(int use)
{
int v;
for(v=V;v>=use;v--)
dp[v] = max(dp[v], dp[v-use]+use);
}
void Every(int use)
{
int v;
for(v=use;v<=V;v++)
dp[v] = max(dp[v], dp[v-use]+use);
}
void Mulitp(int n, int use)
{
int k;
if(use>V)
return;
if(use*n>=V)
Every(use);
else
{
k = 1;
while(k<n)
{
Alone(k*use);
n -= k;
k *= 2;
}
Alone(n*use);
}
}
int main(void)
{
int i, n, s[15], val[15];
while(scanf("%d", &V)!=EOF)
{
memset(dp, 0, sizeof(dp));
scanf("%d", &n);
for(i=1;i<=n;i++)
scanf("%d%d", &s[i], &val[i]);
for(i=1;i<=n;i++)
Mulitp(s[i], val[i]);
printf("%d\n", dp[V]);
}
}