背包问题:
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<n≤50;
- 对于 50 % 50\% 50% 的数据, 0 < n ≤ 1000 0<n \le 1000 0<n≤1000;
- 对于 100 % 100\% 100% 的数据, 0 < n ≤ 1 0 4 0<n \le 10^4 0<n≤104,所有读入的数均属于 [ 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 m≤103 。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ m ≤ 1 0 4 1 \leq m \le 10^4 1≤m≤104, 1 ≤ t ≤ 1 0 7 1 \leq t \leq 10^7 1≤t≤107,且 1 ≤ m × t ≤ 1 0 7 1 \leq m \times t \leq 10^7 1≤m×t≤107, 1 ≤ a i , b i ≤ 1 0 4 1 \leq a_i, b_i \leq 10^4 1≤ai,bi≤104。
这道题是一道 多重背包(完全背包) 的题,这题很简单,随便看着我的公式也可以过,代码如下:
#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 0≤n≤10, 0 ≤ x ≤ 20 0\le x\le 20 0≤x≤20。
- 对于 60 % 60\% 60% 的数据,保证 0 ≤ n , x ≤ 100 0\le n,x\le 100 0≤n,x≤100, 10 < l o s e i , w i n i ≤ 100 10<lose_i,win_i\le 100 10<losei,wini≤100, 0 ≤ u s e i ≤ 5 0\le use_i\le 5 0≤usei≤5。
- 对于 100 % 100\% 100% 的数据,保证 0 ≤ n , x ≤ 1 0 3 0\le n,x\le 10^3 0≤n,x≤103, 0 < l o s e i ≤ w i n i ≤ 1 0 6 0<lose_i\le win_i\le 10^6 0<losei≤wini≤106, 0 ≤ u s e i ≤ 1 0 3 0\le use_i\le 10^3 0≤usei≤103。
这道题没什么难的,但他是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;
}