#背包问题是给定物体价值和重量,给定背包大小,求最优值的问题,大致分三种,01背包,完全背包和多重背包,其中01背包为基础,另外两种都可化为01背包。
#01背包是指所有的物品都只有一件,拥有独自的价值,完全背包是每件物品都有无数件,多重背包是物品有指定的有限数目件。对于完全背包和多重背包,将重复数目的物品拆分成01背包问题下相同价值的物品即可。
#具体动态规划的解法原理这里不再赘述,这篇文章将通过HDO1171的一道例题理一遍背包问题思路。
#问题
Big Event in HDU
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 48724 Accepted Submission(s): 16680
The splitting is absolutely a big event in HDU! At the same time, it is a trouble thing too. All facilities must go halves. First, all facilities are assessed, and two facilities are thought to be same if they have the same value. It is assumed that there is N (0<N<1000) kinds of facilities (different value, different kinds).
A test case starting with a negative integer terminates input and this test case is not to be processed.
这是一道特殊的背包问题,我们要使A和B的和尽量靠近,也就是A和B都尽量靠近总价值的一半,且A大于B。物品是不占空间的,那么可以使总价值一半作为背包容量,这样就能求解出最接近于一半价值的并且小于一半价值的价值总和。
(........明白了使用二维数组并顺利解开这道题后)
画一张表手动画画就会发现,填表的时候每个格的值是通过其左上方的值确定的(背包容量在x方向,从1开始递增,物品序号在y方向的表格),如果我们从右方开始填表,那么第i行第j个格及以后的值都与此格右上方的值无关了,仅仅此格与其正上方的值有关,我们在确定此格值后便可直接用此值覆盖其上方值,故仅需一维数组即可。
使用右方开始填写的顺序还有一个好处,状态转移方程中有对于j(背包容量)的判断,如果是从左开始时在j小于物体重量时都是需要填0的,通常还要写个if-else,但从右往左只需写个for,终止于j<w[i](重量)就行了,代码省了几行,不过需要初始化数组为0,否则会有某些位置没能填上值的错误。
#AC码
#include<iostream>
#include <string.h>
#include <algorithm>
using namespace std;
int va[5555];
int dp[255555];
int main()
{
int n;
while(cin>>n,n>0)
{
memset(dp,0,sizeof(dp));
int sum=0;
int l=1;
int t,a,mid;
for(int i=0;i<n;i++)
{
cin>>a>>t;
sum+=a*t;
while(t--)
va[l++]=a;
}
mid=sum/2;
for(int i=1;i<l;i++)
for(int j=mid;j>=va[i];j--)
dp[j]=max(dp[j],dp[j-va[i]]+va[i]);
cout<<sum-dp[mid]<<" "<<dp[mid]<<endl;
}
}