笔记:0-1背包问题
例题:(已AC)P1048 [NOIP2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有 2 个整数 T(1≤T≤1000)和M(1≤M≤100),用一个空格隔开,T 代表总共能够用来采药的时间,M 代表山洞里的草药的数目。
接下来的 M 行每行包括两个在 1 到 100 之间(包括 1 和 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式
输出在规定的时间内可以采到的草药的最大总价值。
输入输出样例
输入
70 3 71 100 69 1 1 2
输出
3
说明/提示
【数据范围】
- 对于 30% 的数据,M≤10;
- 对于全部的数据,M≤100。
【题目来源】
NOIP 2005 普及组第三题
第一版代码:(感觉这个做法更符合贪心算法?)
#include <iostream>
#include <cstdio>
using namespace std;
struct medicine
{
int time;
int value;
}medicine[1000000]; //一个草药由时间和价值组成;
int main()
{
int T,M,i,sum=0,all_time=0; //sum统计价值,all_time统计时间
cin>>T>>M;
for(i=0;i<M;++i)
{
cin>>medicine[i].time>>medicine[i].value;
}
//输入数据
for(i=0;i<M;)
{
if(medicine[i].time>T)
{
++i; //如果判断超过范围就进行下一个判断
}
else
{
sum=sum+medicine[i].value;
all_time=all_time+medicine[i].time; //统计
if(all_time>=T)
{
break; //超出限定条件就跳出
}
else
{
i++; //否则进行下一个数的查找
}
}
}
cout<<sum<<endl;
return 0;
}
缺少了动态规划中需要的比对删除的动态环节。
第二版代码:(已AC)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1e7+5;
int T,M;
int a[maxn];
struct medicine
{
int time;
int value;
}medicine[1000000];
int max(int a,int b)
{
if(a>b) return a;
else return b;
}
int main()
{
int i,j;
cin>>T>>M;
for(i=1;i<=M;++i)
{
cin>>medicine[i].time>>medicine[i].value;
}
memset(a,0,sizeof(a));
for(int i=1;i<=M;++i)
{
for(int j=T;j>=medicine[i].time;--j)
{
a[j]=max(a[j-medicine[i].time]+medicine[i].value,a[j]);
//0-1背包状态转移方程
}
}
cout<<a[T]<<endl;
return 0;
}
相关题目:
题目描述
uim
口袋里只剩M元(M≤10000)。餐馆虽低端,但是菜品种类不少,有N种(N≤100),第 i 种卖a_i元(a_i≤1000)。每种菜只有一份。
小A
奉行“不把钱吃光不罢休”,所以他点单一定刚好把uim
身上所有钱花完。他想知道有多少种点菜方法。
由于小A
肚子太饿,所以最多只能等待1秒。
输入格式
第一行是两个数字,表示N和M。
第二行起N个正数a_i(可以有相同的数字,每个数字均在1000以内)。
输出格式
一个正整数,表示点菜方案数,保证答案的范围在int之内。
输入输出样例
输入
4 4 1 1 2 2
输出
3
说明/提示
2020.8.29,增添一组 hack 数据 by @yummy
(未AC)第一版代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int a[10000000],b[1000000];
int main()
{
int m,n,i,sum=0,ans=0,j;
scanf("%d %d",&n,&m);
for(i=0;i<n;++i)
{
scanf("%d",&a[i]);
}
memset(b,0,sizeof(b));
for(i=0;i<n;++i)
{
if(ans!=m)
{
for(j=0;j<n;)
{
if(b[j]==0)
{
ans=ans+a[i];
b[j]++;
}
else ++j;
}
}
else
sum++;
}
printf("%d",sum);
return 0;
}
第二版代码:(已AC)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e7+5;
int a[maxn],f[maxn];
int main()
{
int m,n,i,j;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
}
f[0]=1; //!*如果刚好剩下的钱够买一道菜
for(int i=1;i<=n;++i)
{
for(int j=m;j>=a[i];--j)
{
f[j]+=f[j-a[i]];
//状态转移方程(现在的花费种类+=我不点这个菜的时候的花费种类或者我点(二者相同))
}
}
printf("%d",f[m]);
return 0;
}
练习2: (已AC)洛谷 P1049 [NOIP2001 普及组] 装箱问题
【题目描述】
有一个箱子容量为V(正整数,0≤V≤20000),同时有n个物品(0<n≤30,每个物品有一个体积(正整数)。
要求n个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。
【输入格式】
1个整数,表示箱子容量
1个整数,表示有n个物品
接下来n行,分别表示这n个物品的各自体积
【输出格式】
1个整数,表示箱子剩余空间。
【输入输出样例】
输入:
24 6 8 3 12 7 9 7
输出:
0
说明/提示
【题目来源】
NOIP 2001 普及组第四题
已AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e7+5;
int a[maxn],f[maxn];
int main()
{
int V,i,n,rest;
scanf("%d",&V);
scanf("%d",&n);
for(i=1;i<=n;++i)
{
scanf("%d",&a[i]);
}
for(int i=1;i<=n;++i)
{
for(int j=V;j>=a[i];--j)
{
f[j]=max(f[j],f[j-a[i]]+a[i]); //比较装下该物体与不装该物体的最大体积
}
}
rest=V-f[V]; //剩下的体积;
printf("%d",rest);
return 0;
}
rt,使箱子的剩余空间为最小,即求物品总量的最大值,比较装下某物体或不装某物体的体积的比较,只存在选与不选的两种情况,那么这是0-1背包问题。