简单的01背包问题,简单的说就是将已有的资源发挥出最大的价值。
每组测试数据的第一行为两个正整数N和M,表示奖品的个数,以及奖券数。
接下来的N行描述每一行描述一个奖品,其中第i行为两个整数need(i)和value(i),意义如前文所述。
测试数据保证
对于100%的数据,N的值不超过500,M的值不超过10^5
对于100%的数据,need(i)不超过2*10^5, value(i)不超过10^3
直接动态规划
定义int f(int n,int m)为 从第n个物品开始考虑,在还有m个资源的情况下能发挥的最大价值。
则有:
int f(int n,int m)
{
if(n==N) return 0;
int t1=f(n+1,m);
int t2=0;
if(m>=need[n]) t2=f(n+1,m-need[n])+value[n]; //唯一需要注意的就是当前资源足够时,才能发挥对应的价值。有m>=need[n]
return t1>t2?t1:t2;
}
以上代码确实可以解决这个01背包问题。
但是以上代码的运算速度太慢。
而且不能用一般的记忆化,因为所需空间(500*100000*4b)太大。
若将选择考虑顺序倒过来,考虑到在计算的过程中有一下情况:
为求f(n,m) 就得求得 f(n-1,m)和f(n-1,m-need[n])+value[n]。
若当前记录下f(n,m),在求f(n+1,m)的时候,f(n-1,m)和f(n-1,m-need[n])的值就用不着了。
所以用来做记忆化的数组就可以把n这个参数去掉,只需要一维就够了。int ans[M+1];
for(int n=0;n<N;n++)
for(int i=M;i>=need[n];i--)
{
int t1=ans[i];
int t2=ans[i-need[n]]+value[n];
ans[i]=t1>t2?t1:t2;
}
全代码:
#include<cstdio>
#include<cstring>
using namespace std;
int N,M;
int need[501],value[501];
int ans[100001];
int f()
{
for(int n=0;n<N;n++)
for(int i=M;i>=need[n];i--)
{
int t1=ans[i];
int t2=ans[i-need[n]]+value[n];
ans[i]=t1>t2?t1:t2;
}
return ans[M];
}
int main()
{
memset(ans,0,sizeof(ans));
scanf("%d%d",&N,&M);
for(int i=0;i<N;i++)
scanf("%d%d",&need[i],&value[i]);
printf("%d",f());
return 0;
}