#1038 : 01背包
-
5 1000 144 990 487 436 210 673 567 58 1056 897
样例输出
-
2099
描述
且说上一周的故事里,小Hi和小Ho费劲心思终于拿到了茫茫多的奖券!而现在,终于到了小Ho领取奖励的时刻了!
小Ho现在手上有M张奖券,而奖品区有N件奖品,分别标号为1到N,其中第i件奖品需要need(i)张奖券进行兑换,同时也只能兑换一次,为了使得辛苦得到的奖券不白白浪费,小Ho给每件奖品都评了分,其中第i件奖品的评分值为value(i),表示他对这件奖品的喜好值。现在他想知道,凭借他手上的这些奖券,可以换到哪些奖品,使得这些奖品的喜好值之和能够最大。
输入
每个测试点(输入文件)有且仅有一组测试数据。
每组测试数据的第一行为两个正整数N和M,表示奖品的个数,以及小Ho手中的奖券数。
接下来的n行描述每一行描述一个奖品,其中第i行为两个整数need(i)和value(i),意义如前文所述。
测试数据保证
对于100%的数据,N的值不超过500,M的值不超过10^5
对于100%的数据,need(i)不超过2*10^5, value(i)不超过10^3
输出
对于每组测试数据,输出一个整数Ans,表示小Ho可以获得的总喜好值。
题意: 经典的01背包问题, need[i]可以看成重量, value就是价值. 奖券数就是背包的容量.
分析: 用了两种方法来解决这道题,复杂度差别很大.
比较慢的方法:
#include<bitset> //900ms
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int in()
{
int res=0;char c;int f=1;
while((c=getchar())<'0' || c>'9')if(c=='-')f=-1;
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
const int N=100010;
int dp[N*5],v[5555],w[555]; //dp[i]表示 价值和 为i的时候最小的重量是多少
int main()
{
int n=in(),m=in(),sum=0;
for(int i=0;i<n;i++)
{
w[i]=in(),v[i]=in();
sum += v[i];
}
mem(dp,inf); //初始化为无穷大
dp[0]=0;
for(int i=0;i<n;i++)
{
for(int j=sum;j>=v[i];j--) //从sum开始逆向循环,保证每个物品只能被用一次
{
dp[j]=min(dp[j],dp[j-v[i]]+w[i]);
}
}
for(int i=sum;i>=0;i--)
{
if(dp[i]<=m)
{
printf("%d\n",i);
break;
}
}
return 0;
}
比较经典的方法:
由于hihocoder是单组数据, 不用每次给数组赋值0. 多组数据的时候要记得归零
#include<bitset> //195ms
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int in()
{
int res=0;char c;int f=1;
while((c=getchar())<'0' || c>'9')if(c=='-')f=-1;
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res*f;
}
const int N=100010;
int dp[N],w[555],v[555]; //重量为i的时候最大的价值是多少
int main()
{
int n=in(),m=in();
for(int i=0;i<n;i++)
{
w[i]=in(),v[i]=in();
}
for(int i=0;i<n;i++)
{
for(int j=m;j>=w[i];j--) //从m开始逆向循环,保证每个物品只能用一次
{
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
printf("%d\n",dp[m]);
return 0;
}
第一种方法在这里显然是慢了很多的,但是要是w[i]与背包容量的值变得很大, 而value值变得很小, 就体现出它的优势了.比如下面这题
Accept: 15 Submit: 32
Time Limit: 3000 mSec Memory Limit : 32768 KB
Problem Description
Given a set of n items, each with a weight w[i] and a value v[i], determine a way to choose the items into a knapsack so that the total weight is less than or equal to a given limit B and the total value is as large as possible. Find the maximum total value. (Note that each item can be only chosen once).
Input
The first line contains the integer T indicating to the number of test cases.
For each test case, the first line contains the integers n and B.
Following n lines provide the information of each item.
The i-th line contains the weight w[i] and the value v[i] of the i-th item respectively.
1 <= number of test cases <= 100
1 <= n <= 500
1 <= B, w[i] <= 1000000000
1 <= v[1]+v[2]+...+v[n] <= 5000
All the inputs are integers.
Output
For each test case, output the maximum value.
Sample Input
Sample Output
题意: 依然是01背包,,,
分析:数据变了, 换一种想法就行了.
#include<bitset> //1000+ms
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
inline int in()
{
int res=0;char c;
while((c=getchar())<'0' || c>'9');
while(c>='0' && c<='9')res=res*10+c-'0',c=getchar();
return res;
}
const int N=100010;
int dp[5001],w[505],v[505]; //dp表示 价值和 为i 的时候重量的最小值
int main()
{
int T=in();
while(T--)
{
int n=in(),m=in();
int sum=0;
for(int i=0;i<n;i++)
{
w[i]=in(),v[i]=in();
sum += v[i];
}
mem(dp,inf); //初始化为无穷大
dp[0]=0;
for(int i=0;i<n;i++)
{
for(int j=sum;j>=v[i];j--)
{
dp[j]=min(dp[j],dp[j-v[i]]+w[i]);
}
}
for(int i=sum;i>=0;i--)
{
if(dp[i]<=m)
{
printf("%d\n",i);
break;
}
}
}
return 0;
}