POJ 3628 Bookshelf 2(DP:01背包)
http://poj.org/problem?id=3628
题意:有N个数字和一个H上限,问你这N个数字怎么取(每个取或者不取)可以使得被取的数总和最小且超过N.求这个最小和与N的差值.
分析:N个数字的和为S且S肯定>=H,我们只需要令X=S-H,然后求背包容量(这里的容量指的是数的价值和不超过X)为X的背包最多能放多少价值,那么剩下的那部分没放的数价值和就肯定>=H且最小.
所以求出容量X的背包能放的最大价值-X的绝对值就是所求.
d[i][j]=x表示放完前i个数容量不超过j能获得的最大和值为x.
d[i][j] =max(d[i-1][j],d[i-1][j-wi]+wi).j>=wi,wi为第i个数的值
初值为:d=0,要用滚动数组
程序实现有两个地方需要注意:
1. memset(d,0,sizeof(d)); 这句如果加了就会超内存,不明白?
2. 循环时j的下界是W[i],而不是0.
AC代码:0ms
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
const int MAXN=20000000+1;
int w[30],d[MAXN];
int main()
{
int n,h;
while(scanf("%d%d",&n,&h)==2)
{
int sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
sum +=w[i];
}
//memset(d,0,sizeof(d));//这句不注释掉就会超内存?为什么
int m=sum-h;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=w[i];j--)//这里注意细节,j的下限是w[i]
{
if(j>=w[i]) d[j] = max(d[j],d[j-w[i]]+w[i]);
}
}
printf("%d\n",m-d[m]);
}
return 0;
}