HPU背包练习Level 1

01背包

P1048 采药
题目描述

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

输入格式

第一行有 222 个整数 TTT(1≤T≤10001 \le T \le 10001≤T≤1000)和 MMM(1≤M≤1001 \le M \le 1001≤M≤100),用一个空格隔开,TTT 代表总共能够用来采药的时间,MMM 代表山洞里的草药的数目。

接下来的 MMM 行每行包括两个在 111 到 100100100 之间(包括 111 和 100100100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

输出格式

输出在规定的时间内可以采到的草药的最大总价值。
输入输出样例
输入 #1

70 3
71 100
69 1
1 2

输出 #1

3

是一个01背包问题
注意的是,01背包问题的话,第二层循环要从容量开始到这个物品的容量结束,
因为01背包是从上一层的状态转移过来的

#include<iostream>
#include<cstring>
using namespace std;
int t[1234],w[1234];
int dp[1234];
int main()
{
	int tt,ww;
	cin>>tt>>ww;
	for(int i=0;i<ww;i++) cin>>t[i]>>w[i];
	for(int i=0;i<ww;i++)
	{
		for(int j=tt;j>=t[i];j--)
		{
			dp[j]=max(dp[j],dp[j-t[i]]+w[i]);
		}
	}
	cout<<dp[tt]<<endl;
	return 0;
}
P1060 开心的金明
题目描述

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过NNN元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的NNN元。于是,他把每件物品规定了一个重要度,分为555等:用整数1−51-51−5表示,第555等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过NNN元(可以等于NNN元)的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第jjj件物品的价格为v[j]v_[j]v[​j],重要度为w[j]w_[j]w[​j],共选中了kkk件物品,编号依次为j1,j2,…,jkj_1,j_2,…,j_kj1​,j2​,…,jk​,则所求的总和为:v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]v_[j_1] \times w_[j_1]+v_[j_2] \times w_[j_2]+ …+v_[j_k] \times w_[j_k]v[​j1​]×w[​j1​]+v[​j2​]×w[​j2​]+…+v[​jk​]×w[​jk​]。

请你帮助金明设计一个满足要求的购物单。

输入格式

第一行,为222个正整数,用一个空格隔开:nmn mnm(其中N(<30000)N(<30000)N(<30000)表示总钱数,m(<25)m(<25)m(<25)为希望购买物品的个数。)

从第222行到第m+1m+1m+1行,第jjj行给出了编号为j−1j-1j−1的物品的基本数据,每行有222个非负整数vp v pvp(其中vvv表示该物品的价格(v≤10000)(v \le 10000)(v≤10000),ppp表示该物品的重要度(1−51-51−5)

输出格式

111个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(<100000000)(<100000000)(<100000000)。
输入输出样例
输入 #1

1000 5
800 2
400 5
300 5
400 3
200 2

输出 #1

3900

这个也是一个01背包问题,但是要注意每一个物品的价值

#include<iostream>
#include<cstring>
using namespace std;
int t[987234],w[987234];
int dp[987234];
int main()
{
	int tt,ww;
	cin>>tt>>ww;
	for(int i=0;i<ww;i++) 
	{
		cin>>t[i]>>w[i];
		w[i]=t[i]*w[i];
	}
	for(int i=0;i<ww;i++)
	{
		for(int j=tt;j>=t[i];j--)
		{
			dp[j]=max(dp[j],dp[j-t[i]]+w[i]);
		}
	}
	cout<<dp[tt]<<endl;
	return 0;
}
P1049 装箱问题
题目描述

有一个箱子容量为VVV(正整数,0≤V≤200000 \le V \le 200000≤V≤20000),同时有nnn个物品(0<n≤300<n \le 300<n≤30,每个物品有一个体积(正整数)。

要求nnn个物品中,任取若干个装入箱内,使箱子的剩余空间为最小。

输入格式

111个整数,表示箱子容量

111个整数,表示有nnn个物品

接下来nnn行,分别表示这nnn个物品的各自体积

输出格式

111个整数,表示箱子剩余空间。
输入输出样例
输入 #1

24
6
8
3
12
7
9
7

输出 #1

0

01背包问题,但是让求的是剩下的容量,所以最后要减一下

#include<iostream>
#include<cstring>
using namespace std;
int v[72345],w[72345];
int dp[72345];
int main()
{
	int vv,ww;
	cin>>vv>>ww;
	for(int i=0;i<ww;i++) 
	{
		cin>>v[i];
		w[i]=v[i];
	}
	for(int i=0;i<ww;i++)
	{
		for(int j=vv;j>=v[i];j--)
		{
			dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
		}
	}
	cout<<vv-dp[vv]<<endl;
	return 0;
}
P1802 5倍经验日
题目背景

现在乐斗有活动了!每打一个人可以获得5倍经验!absi2011却无奈的看着那一些比他等级高的好友,想着能否把他们干掉。干掉能拿不少经验的。

题目描述

现在absi2011拿出了x个迷你装药物(嗑药打人可耻….),准备开始与那些人打了 由于迷你装一个只能管一次,所以absi2011要谨慎的使用这些药,悲剧的是,没到达最少打败该人所用的属性药了他打人必输>.<所以他用2个药去打别人,别人却表明3个药才能打过,那么相当于你输了并且这两个属性药浪费了。
现在有n个好友,有输掉拿的经验、赢了拿的经验、要嗑几个药才能打过。求出最大经验(注意,最后要乘以5)

输入格式

第一行两个数,n和x 后面n行每行三个数,分别表示输了拿到的经验(lose[i])、赢了拿到的经验(win[i])、打过要至少使用的药数量(use[i])。

输出格式

一个整数,最多获得的经验
输入输出样例
输入 #1

6 8
21 52 1
21 70 5
21 48 2
14 38 3
14 36 1
14 36 2

输出 #1

1060

话说这个题不能把不嗑药的当作0,然后是2n个背包,因为这样可能一个人被打了两次

#include<iostream>
#include<cstring>
using namespace std;
int vv[12345][2],ww[12454];
long long dp[123445];
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++)  cin>>vv[i][0]>>vv[i][1]>>ww[i];
	for(int i=0;i<n;i++)
	{
		for(int j=m;j>=0;j--)
		{//如果大于所需要的药的量,就判断是嗑药拿赢的经验好,还是不嗑药拿输的经验好
			if(j>=ww[i]) dp[j]=max(dp[j]+vv[i][0],dp[j-ww[i]]+vv[i][1]);
			else dp[j]=dp[j]+vv[i][0];//如果小于,那就只能那输的经验了
		}
	}
	cout<<dp[m]*5<<endl;
	return 0;
}
P1507 NASA的食物计划
题目背景

NASA(美国航空航天局)因为航天飞机的隔热瓦等其他安全技术问题一直大伤脑筋,因此在各方压力下终止了航天飞机的历史,但是此类事情会不会在以后发生,谁也无法保证,在遇到这类航天问题时,解决方法也许只能让航天员出仓维修,但是多次的维修会消耗航天员大量的能量,因此NASA便想设计一种食品方案,让体积和承重有限的条件下多装载一些高卡路里的食物.

题目描述

航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里,在告诉你体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次.

输入格式

第一行 两个数 体积最大值(<400)和质量最大值(<400)

第二行 一个数 食品总数N(<50).

第三行-第3+N行

每行三个数 体积(<400) 质量(<400) 所含卡路里(<500)

输出格式

一个数 所能达到的最大卡路里(int范围内)
输入输出样例
输入 #1

320 350
4
160 40 120
80 110 240
220 70 310
40 400 220

输出 #1

550

这个题要三重循环了
第一层循环n个物品,第二层循环体积,第三重循环质量,
因为是每件物品只能用一次,所以是一个01背包问题,所以第二层,第三层循环的时候要从最大容量开始循环

#include<iostream>
#include<cstring>
using namespace std;
int v[1234],w[1234],c[1234];
int dp[1234][1234];
int main()
{
	int maxv,maxw,n;
	cin>>maxv>>maxw>>n;
	for(int i=0;i<n;i++)
		cin>>v[i]>>w[i]>>c[i];
	for(int i=0;i<n;i++)
	{
		for(int j=maxv;j>=v[i];j--)
		{
			for(int k=maxw;k>=w[i];k--)
			dp[j][k]=max(dp[j][k],dp[j-v[i]][k-w[i]]+c[i]);
		}
	}
	cout<<dp[maxv][maxw]<<endl;
	return 0;
}

分组背包

P1757 通天之分组背包
题目背景

直达通天路·小A历险记第二篇

题目描述

自01背包问世之后,小A对此深感兴趣。一天,小A去远游,却发现他的背包不同于01背包,他的物品大致可分为k组,每组中的物品相互冲突,现在,他想知道最大的利用价值是多少。

输入格式

两个数m,n,表示一共有n件物品,总重量为m

接下来n行,每行3个数ai,bi,ci,表示物品的重量,利用价值,所属组数

输出格式

一个数,最大的利用价值
输入输出样例
输入 #1

45 3
10 10 1
10 5 1
50 400 2

输出 #1

10

分组背包,也是要三重循环,
第一层循环组数,第二层循环容量,第三重循环每一组的每一个物品
但是分组背包里第二重循环的时候要从m到0;

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
vector<int> vv[1234],ww[1234];
int dp[1234];
int t;
int main()
{
	int n,m;
	cin>>m>>n;
	for(int i=0;i<n;i++)
	{
		int v,w,s;
		cin>>v>>w>>s;
		t=max(t,s);
		vv[s].push_back(v);ww[s].push_back(w);
	}
	for(int i=1;i<=t;i++)
	{
		for(int j=m;j>=0;j--)
		{
			for(int k=0;k<vv[i].size();k++)
			if(j>=vv[i][k]) dp[j]=max(dp[j],dp[j-vv[i][k]]+ww[i][k]);
		}
	}
	cout<<dp[m]<<endl;
	return 0;
}
AcWing 9. 分组背包问题

有 N 组物品和一个容量是 V的背包。
每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j是组内编号。
求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式

第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。
接下来有 N组数据: 每组数据第一行有一个整数 Si,表示第 i个物品组的物品数量;
每组数据接下来有 Si行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;

输出格式

输出一个整数,表示最大价值。
数据范围

0<N,V≤100

0<Si≤100
0<vij,wij≤100

输入样例

3 5
2
1 2
2 4
1
3 4
1
4 5

输出样例:

8

#include<iostream>
#include<cstring>
using namespace std;
int vv[12345],ww[12345];
int dp[12345];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=0;i<n;i++)
    {
        int s;
        cin>>s;
        for(int j=0;j<s;j++) cin>>vv[j]>>ww[j];
        for(int j=m;j>=0;j--)
        {
            for(int k=0;k<s;k++)
            if(j>=vv[k]) dp[j]=max(dp[j],dp[j-vv[k]]+ww[k]);
        }
    }
    cout<<dp[m]<<endl;
    return 0;
}

完全背包

AcWing 3. 完全背包问题

有 N 种物品和一个容量是 V的背包,每种物品都有无限件可用。
第 i种物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式

输出一个整数,表示最大价值。
数据范围

0<N,V≤1000

0<vi,wi≤1000

输入样例

4 5
1 2
2 4
3 4
4 5

输出样例:

10

完全背包与01背包类似,转移方程时只有第二重循环不一样,
完全背包问题,每一个物品都有无限次可以选择,
第二重循环时应该从v[i]循环到m

#include<iostream>
#include<cstring>
using namespace std;
int v[12345],w[12345];
int dp[12345];
int main()
{
    int n,maxv;
    cin>>n>>maxv;
    for(int i=0;i<n;i++) cin>>v[i]>>w[i];
    for(int i=0;i<n;i++)
    {
        for(int j=v[i];j<=maxv;j++)
        dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    }
    cout<<dp[maxv]<<endl;
    return 0;
}
P1832 A+B Problem 【完全背包+计数】
题目背景

·题目名称是吸引你点进来的

·实际上该题还是很水的

题目描述

·1+1=? 显然是2

·a+b=? 1001回看不谢

·哥德巴赫猜想 似乎已呈泛滥趋势

·以上纯属个人吐槽

·给定一个正整数n,求将其分解成若干个素数之和的方案总数。

输入格式

一行:一个正整数n

输出格式

一行:一个整数表示方案总数
输入输出样例
输入 #1

7

输出 #1

3

这个题想起了强哥的话,从0个数字中选择n个方法是1,这是存在一种方法的,是方法数,并不是结果

所以这个题的dp[0]=1;

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
bool st[1234];
int primes[1245];
int cnt;
long long dp[12345];
void get_prime(int n)
{
	for(int i=2;i<=n;i++)
	{
		if(!st[i]) 
		{
			primes[cnt++]=i;
			for(int j=i+i;j<=n;j+=i) st[j]=1;
		}
	}
}
int main()
{
	int n;
	cin>>n;
	get_prime(n);
	dp[0]=1;
	for(int i=0;i<cnt;i++)
	{
		for(int j=primes[i];j<=n;j++)
		{
			dp[j]=dp[j]+dp[j-primes[i]];
		}
	}
	cout<<dp[n]<<endl;
	return 0;
 } 

多重背包

AcWing 5. 多重背包问题 II

有 N 种物品和一个容量是 V的背包。
第 i种物品最多有 si 件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式

第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。
接下来有 N行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围

0<N≤1000
0<V≤2000
0<vi,wi,si≤2000

提示:

本题考查多重背包的二进制优化方法。
输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

这个题用二进制将每个物品的件数分解之后,就可以直接做一遍01背包就行了

#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+7;
int vv[N],ww[N],dp[N];

int main()
{
    int n,m;
    int cnt=0;
    cin>>n>>m;
    while(n--)
    {
        int v,w,s;int x=1;
        cin>>v>>w>>s;
        while(x<=s)
        {
            vv[cnt]=v*x;
            ww[cnt]=w*x;
            cnt++;
            s-=x;
            x=x*2;
        }
        if(s) 
        {
            vv[cnt]=v*s;
            ww[cnt]=w*s;
            cnt++;
        }
    }
    for(int i=0;i<cnt;i++)
    {
        for(int j=m;j>=vv[i];j--)
        dp[j]=max(dp[j],dp[j-vv[i]]+ww[i]);
    }
    cout<<dp[m]<<endl;
    return 0;
}
01背包

每个物品只能选一次,
第二重循环要从容量m循环到v[i];

完全背包

每个物品都有无限多个,
第二重循环要从v[i]循环到容量m

多重背包

每个背包都有s个,
先把s用二进制分解,然后再把分解之后的背包做一遍01背包

分组背包

每个组里的物品是互斥的,在同一组里选了这个就不能选其他的
三重循环,第一层循环组数,第二重循环容量,从容量m开始循环到0,第三重循环每一组的每一个物品
然后判断如果当前容量大于物品的体积就更新

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值