(笔记未完成)【动态规划】背包问题:0-1背包问题

 笔记: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; 
}

相关题目:

练习1:(已AC)洛谷 P1164 小A点菜

题目描述

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背包问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值