动态规划·背包问题

背包问题:

1.01背包:

二位写法:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+c[i]);

简化成一维的时候 j j j 要倒序遍历:

dp[j]=max(dp[j],dp[j-w[i]]+c[i]);

2.完全背包:

dp[i][j]:前 i i i 件物品,体积不超过 i i i 能获得的最大价值

dp[i][j]=max(dp[i-1][j],dp[i][j-w[i]]+c[i]);

化简成一维的时候 j j j 要进行正序遍历:

dp[j]=max(dp[j],dp[j-w[i]]+c[i]);

精卫填海

题目描述

本题为改编题。

发鸠之山,其上多柘木。有鸟焉,其状如乌,文首,白喙,赤足,名曰精卫,其名自詨。是炎帝之少女,名曰女娃。女娃游于东海,溺而不返,故为精卫。常衔西山之木石,以堙于东海。——《山海经》

精卫终于快把东海填平了!只剩下了最后的一小片区域了。同时,西山上的木石也已经不多了。精卫能把东海填平吗?

事实上,东海未填平的区域还需要至少体积为 v v v 的木石才可以填平,而西山上的木石还剩下 n n n 块,每块的体积和把它衔到东海需要的体力分别为 k k k m m m。精卫已经填海填了这么长时间了,她也很累了,她还剩下的体力为 c c c

输入格式

输入文件的第一行是三个整数: v , n , c v,n,c v,n,c

从第二行到第 n + 1 n+1 n+1 行分别为每块木石的体积和把它衔到东海需要的体力。

输出格式

输出文件只有一行,如果精卫能把东海填平,则输出她把东海填平后剩下的最大的体力,否则输出 Impossible(不带引号)。

样例 #1

样例输入 #1

100 2 10
50 5
50 5

样例输出 #1

0

样例 #2

样例输入 #2

10 2 1
50 5
10 2

样例输出 #2

Impossible

提示

数据范围及约定

  • 对于 20 % 20\% 20% 的数据, 0 < n ≤ 50 0<n \le 50 0<n50
  • 对于 50 % 50\% 50% 的数据, 0 < n ≤ 1000 0<n \le 1000 0<n1000
  • 对于 100 % 100\% 100% 的数据, 0 < n ≤ 1 0 4 0<n \le 10^4 0<n104,所有读入的数均属于 [ 0 , 1 0 4 ] [0,10^4] [0,104],最后答案不大于 c c c

这道题是一道01背包的题,我们要判断第 i i i 块石头要不要叼去东海,我们要判断每次叼去的石头的体力要是 最小值
核心代码如下:

for(int i=1;i<=n;i++){
	for(int j=c;j>=m[i];j--){
		dp[j]=max(dp[j],dp[j-m[i]]+k[i]);	//k是体积,m是消耗的体力
	}
}

如果你还是不会做的话,就参考下面的代码:

#include <bits/stdc++.h>
using namespace std;
const int N=1e4+10;	//数据范围是10的4次方
typedef long long ll;	//把long long用ll表示
ll v,n,c,sum;
ll k[N],m[N],dp[N];
int main()
{
	scanf("%lld%lld%lld",&v,&n,&c);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&k[i],&m[i]);
		sum+=k[i];
	}
	if(sum<v){	//先把绝对不能满足的筛掉
		cout<<"Impossible"<<endl;
		return 0;
	}
	for(int i=1;i<=n;i++){
		for(int j=c;j>=m[i];j--){
			dp[j]=max(dp[j],dp[j-m[i]]+k[i]);
		}
	}
	for(int i=0;i<=v;i++){	//查找
		if(dp[i]>=v){	//如果查找到了就输出,然后跳出
			cout<<c-i<<endl;
			return 0;
		}
	}
	cout<<"Impossible"<<endl;	//如果没有找到,那就输出Impossible
	return 0;
}

疯狂的采药

题目背景

此题为纪念 LiYuxiang 而生。

题目描述

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

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

此题和原题的不同点:

1 1 1. 每种草药可以无限制地疯狂采摘。

2 2 2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

输入格式

输入第一行有两个整数,分别代表总共能够用来采药的时间 t t t 和代表山洞里的草药的数目 m m m

2 2 2 到第 ( m + 1 ) (m + 1) (m+1) 行,每行两个整数,第 ( i + 1 ) (i + 1) (i+1) 行的整数 a i , b i a_i, b_i ai,bi 分别表示采摘第 i i i 种草药的时间和该草药的价值。

输出格式

输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

样例 #1

样例输入 #1

70 3
71 100
69 1
1 2

样例输出 #1

140

提示

数据规模与约定
  • 对于 30 % 30\% 30% 的数据,保证 m ≤ 1 0 3 m \le 10^3 m103
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ m ≤ 1 0 4 1 \leq m \le 10^4 1m104 1 ≤ t ≤ 1 0 7 1 \leq t \leq 10^7 1t107,且 1 ≤ m × t ≤ 1 0 7 1 \leq m \times t \leq 10^7 1m×t107 1 ≤ a i , b i ≤ 1 0 4 1 \leq a_i, b_i \leq 10^4 1ai,bi104

这道题是一道 多重背包(完全背包) 的题,这题很简单,随便看着我的公式也可以过,代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;	//大家考试的时候不要定义一水long long,容易MLE(超空间)
const int N=1e7+10;
ll a[N],b[N],dp[N];
int main()
{
	ll t,m;
	scanf("%lld%lld",&t,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%lld%lld",&a[i],&b[i]);
	}
	for(int i=1;i<=m;i++)
	{
		for(int j=a[i];j<=t;j++)
		{
			dp[j]=max(dp[j],dp[j-a[i]]+b[i]);	//01背包找最大价值
		}
	}
	cout<<dp[t]<<endl;	//输出最后一个数就是结果,因为dp有点像递推
	return 0;
}

5 倍经验日

题目背景

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

题目描述

现在 absi2011 拿出了 x x x 个迷你装药物(嗑药打人可耻…),准备开始与那些人打了。

由于迷你装药物每个只能用一次,所以 absi2011 要谨慎的使用这些药。悲剧的是,用药量没达到最少打败该人所需的属性药药量,则打这个人必输。例如他用 2 2 2 个药去打别人,别人却表明 3 3 3 个药才能打过,那么相当于你输了并且这两个属性药浪费了。

现在有 n n n 个好友,给定失败时可获得的经验、胜利时可获得的经验,打败他至少需要的药量。

要求求出最大经验 s s s,输出 5 s 5s 5s

输入格式

第一行两个数, n n n x x x

后面 n n n 行每行三个数,分别表示失败时获得的经验 l o s e i \mathit{lose}_i losei,胜利时获得的经验 w i n i \mathit{win}_i wini 和打过要至少使用的药数量 u s e i \mathit{use}_i usei

输出格式

一个整数,最多获得的经验的五倍。

样例 #1

样例输入 #1

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

样例输出 #1

1060

提示

【Hint】

五倍经验活动的时候,absi2011 总是吃体力药水而不是这种属性药。

【数据范围】

  • 对于 10 % 10\% 10% 的数据,保证 x = 0 x=0 x=0
  • 对于 30 % 30\% 30% 的数据,保证 0 ≤ n ≤ 10 0\le n\le 10 0n10 0 ≤ x ≤ 20 0\le x\le 20 0x20
  • 对于 60 % 60\% 60% 的数据,保证 0 ≤ n , x ≤ 100 0\le n,x\le 100 0n,x100 10 < l o s e i , w i n i ≤ 100 10<lose_i,win_i\le 100 10<losei,wini100 0 ≤ u s e i ≤ 5 0\le use_i\le 5 0usei5
  • 对于 100 % 100\% 100% 的数据,保证 0 ≤ n , x ≤ 1 0 3 0\le n,x\le 10^3 0n,x103 0 < l o s e i ≤ w i n i ≤ 1 0 6 0<lose_i\le win_i\le 10^6 0<loseiwini106 0 ≤ u s e i ≤ 1 0 3 0\le use_i\le 10^3 0usei103

这道题没什么难的,但他是5倍, i n t int int 1 0 9 10_9 109 还可以,但是装 5 × 1 0 9 5×{10_9} 5×109 就爆 i n t int int 了,所以最后乘5的时候要强行转换成 l o n g long long l o n g long long 就行了,代码如下:

#include <bits/stdc++.h>
using namespace std;
int dp[1100][1100];
int win[1100],lose[1100],use[1100];
int main()
{
    int n,m;
    long long ans;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
    	scanf("%d%d%d",&lose[i],&win[i],&use[i]);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
        	if(j>=use[i])	//判断
        	{
        		dp[i][j]=max(dp[i-1][j]+lose[i],dp[i-1][j-use[i]]+win[i]);
			}
			else
			{
				dp[i][j]=dp[i-1][j]+lose[i];
			}
        }
    }
    printf("%lld",(long long)5*dp[n][m]);	//最后要强行转换成long long
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值