您的位置:首页 > 其它

动态规划之01背包

2015-12-29 23:01 169 查看

#1038 : 01背包

时间限制:20000ms
单点时限:1000ms
内存限制:256MB

描述

且说上一周的故事里,小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可以获得的总喜好值。

样例输入
5 1000
144 990
487 436
210 673
567 58
1056 897

样例输出
2099


题意: 经典的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
,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值变得很小, 就体现出它的优势了.比如下面这题


Problem 2214 Knapsack problem


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
<= 5000

All the inputs are integers.



Output

For each test case, output the maximum value.



Sample Input

15 1512 42 21 14 101 2



Sample Output

15

题意: 依然是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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: