问题描述
有一个箱子容量为V(正整数,0<=V<=20000),同时有n个物品(0<n<=30),每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
输入格式
第一行为一个整数,表示箱子容量;
第二行为一个整数,表示有n个物品;
接下来n行,每行一个整数表示这n个物品的各自体积。
输出格式
一个整数,表示箱子剩余空间。
样例输入
24
6
8
3
12
7
9
7
样例输出
0
关于这一题,有两种解法,一种是利用一维数组dp[]来解决,另一种是利用二维数组dp[][]来解决,我们先来讲第二种,比较好理解
dp[i][j]表示在前i个物品在容量为j的体积中所存放的最大价值,只不过这里的最大价值等价与最大体积,我们在这里应该知道这应该是一个背包问题,对于一个物品,存在着两种情况,放与不放,如果不放则dp[i][j] == dp[i-1][j],因为你们没有放物体,则在逻辑上等价于前一物体,如果放则dp[i][j] == dp[i-1][j-a[i]]+a[i],在前一状态的基础上,加入物体,剩余空间必然减少,价值增大,但是两者又是等价,则可以如是,这一点很难想,也应该是我们所说的背包问题,具体问题请看代码:
#include<iostream>
using namespace std;
int v, n, a[31], dp[31][10000];
/*
dp[i][j]表示前i个物品放入容量为j的背包中所得到的最大值
采用二维数组更好理解一些,但是其空间与时间的开销更大
*/
int main()
{
int i, j;
cin>>v>>n;
for(i = 1; i <= n; i++)
cin>>a[i];
for(i = 1; i <= n; i++)
dp[i][0] = 0;
for(j = 1; j <= v; j++)
dp[0][j] = 0;
for(i = 1; i <= n; i++)
{
for(j = 1; j <= v; j++)
{
if(j < a[i])//装不下
dp[i][j] = dp[i-1][j];
else
dp[i][j] = max(dp[i-1][j], dp[i-1][j-a[i]]+a[i]);
}
}
printf("%d", v-dp[n][v]);
return 0;
}
但是这会超时,只有80%的正确率。
采用一种更简单的方式,只需要用一维数组即可,dp[j]表示在容量剩余为j的体积中所能装入的最大价值,同样有放与不放两种状态,不放则不变即dp[j] == dp[j],放则dp[j] = dp[j-a[i]]+a[i],也是属于背包问题,剩余容量减少,但是价值增大,两者也是等价,具体问题请看代码:
#include<iostream>
#include<cstring>
using namespace std;
int v, n, a[31], dp[20005];
int main()
{
int i, j;
cin>>v>>n;
memset(dp, 0, sizeof(dp));
for(i = 0; i < n; i++)
{
cin>>a[i];
/*
针对一个物品只有两种状态,装与不装,这里的j表示剩余容量
如果装箱,则背包价值为dp[j-a[i]]+a[i],如果不装箱,则为dp[j]
所以dp[j] = max(dp[j], dp[j-a[i]]+a[i])(伪背包问题)
//不太好理解可以采用二维数组解题
*/
for(j = v; j >= a[i]; j--)
{
dp[j] = max(dp[j], dp[j-a[i]]+a[i]);
}
}
printf("%d", v-dp[v]);
return 0;
}
如果还是不太好理解可以先查询关于背包问题的解决方案。