疯狂的采药
题目背景
此题为纪念 LiYuxiang 而生。
题目描述
LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是 LiYuxiang,你能完成这个任务吗?
此题和原题的不同点:
-
每种草药可以无限制地疯狂采摘。
-
药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!
输入格式
输入第一行有两个整数,分别代表总共能够用来采药的时间 t 和代表山洞里的草药的数目 m。
第 2 到第 (m + 1) 行,每行两个整数,第 (i + 1) 行的整数 a_i, b_i 分别表示采摘第 i 种草药的时间和该草药的价值。
输出格式
输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
样例 #1
样例输入 #1
70 3 71 100 69 1 1 2
样例输出 #1
140
提示
数据规模与约定
-
对于 30% 的数据,保证 m <=10^3 。
-
对于 100% 的数据,保证 1 <= m <= 10^4,1 <= t <= 10^7,且 1 <= m *t <= 10^7,1 <= a_i, b_i <= 10^4。
解题思路:经典的完全背包问题
用一维数组优化,正序遍历
完整代码如下:
#include <bits/stdc++.h> using namespace std; const int maxn=1e7+5; int t,m; int w[maxn],v[maxn]; long long f[maxn]; int main() { cin>>t>>m; for(int i=1; i<=m; i++) cin>>w[i]>>v[i]; for(int i=1; i<=m; i++){ for(int j=w[i]; j<=t; j++){ f[j]=max(f[j],f[j-w[i]]+v[i]); } } cout<<f[t]; return 0; }
樱花
题目背景
《爱与愁的故事第四弹·plant》第一章。
题目描述
爱与愁大神后院里种了 n 棵樱花树,每棵都有美学值 C_i(0 <= C_i <= 200)。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看 A_i(0 < A_i <= 100) 遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间 T_i(0 <= T_i \le 100)。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。
输入格式
共 n+1行:
第 1 行:现在时间 T_s(几时:几分),去上学的时间 T_e(几时:几分),爱与愁大神院子里有几棵樱花树 n。这里的 T_s,T_e 格式为:hh:mm
,其中 0 <= hh <= 23,0 <= mm <= 59,且 hh,mm,n均为正整数。
第 2 行到第 n+1 行,每行三个正整数:看完第 i 棵树的耗费时间 T_i,第 i 棵树的美学值 C_i,看第 i 棵树的次数 P_i(P_i=0 表示无数次,P_i 是其他数字表示最多可看的次数 P_i)。
输出格式
只有一个整数,表示最大美学值。
样例 #1
样例输入 #1
6:50 7:00 3 2 1 0 3 3 1 4 5 4
样例输出 #1
11
提示
100% 数据:T_e-T_s <=1000(即开始时间距离结束时间不超过 1000 分钟),n < 10000。保证 T_e,T_s 为同一天内的时间。
样例解释:赏第一棵樱花树一次,赏第三棵樱花树 2 次。
解题思路:多重背包,完全背包,01背包(特殊的多重背包)组成的混合背包
可以把完全背包看成一个很大的数,然后用二进制拆分把所有数拆成01背包
01背包的一维数组优化要倒叙覆盖
完整代码如下:
#include <bits/stdc++.h> using namespace std; const int maxn=1e7+5,p=1e7; int w[maxn],v[maxn],a[maxn],b[maxn],c[maxn],f[maxn],cnt,n; void optimize(); int main() { char x; int h1,m1,h2,m2; cin>>h1>>x>>m1>>h2>>x>>m2>>n; int T; T=(h2*60+m2)-(h1*60+m1); for(int i=1; i<=n; i++){ cin>>a[i]>>b[i]>>c[i]; if(c[i]==0) c[i]=p;//无数次 } optimize(); //二进制优化 for(int i=1; i<=cnt; i++){ for(int j=T; j>=w[i]; j--){ f[j]=max(f[j],f[j-w[i]]+v[i]); } } //01背包的一维数组 cout<<f[T]; return 0; } void optimize() { for(int i=1; i<=n; i++){ int k=1; while(k<=c[i]){ v[++cnt]=k*b[i]; w[cnt]=k*a[i]; c[i]-=k; k*=2; } if(c[i]>0){ v[++cnt]=c[i]*b[i]; w[cnt]=c[i]*a[i]; } } }
摆花
题目描述
小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 n 种花,从 1 到 n 标号。为了在门口展出更多种花,规定第 i 种花不能超过 a_i 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。
输入格式
第一行包含两个正整数 n 和 m,中间用一个空格隔开。
第二行有 n 个整数,每两个整数之间用一个空格隔开,依次表示 a_1,a_2, ...... ,a_n。
输出格式
一个整数,表示有多少种方案。注意:因为方案数可能很多,请输出方案数对 10^6+7 取模的结果。
样例 #1
样例输入 #1
2 4 3 2
样例输出 #1
2
提示
【数据范围】
对于 20% 数据,有 0<= n <= 8,0<= m <= 8,0 <= a_i <= 8。
对于 50% 数据,有 0<=n <= 20,0<=m <= 20,0 <= a_i <= 20。
对于 100% 数据,有 0<=n <= 100,0<=m <= 100,0 <= a_i <= 100。
解题思路:dp
每次考虑摆第i种花时,摆完之后已经有了j种
第i种花有可能摆了0,1,2,... ,min(a_i,j)盆
那么f_i,j=(f_i-1,j+f_i-1,j-1+...+f_i-1,j-min(a_i,j)) % mod
f_0,0=1
完整代码如下:
#include <bits/stdc++.h> using namespace std; const int mod=1000007; int a[10005],f[10005]; int main() { int n,m; cin>>n>>m; for(int i=1; i<=n; i++) cin>>a[i]; f[0]=1; for(int i=1; i<=n; i++){ for(int j=m; j>=0; j--){ for(int k=1; k<=min(j,a[i]); k++){ f[j]=(f[j]+f[j-k])%mod; } } } cout<<f[m]; return 0; } //01背包
金明的预算方案
题目描述
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 $n$ 元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 | 附件 |
---|---|
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、1 个或 2 个附件。每个附件对应一个主件,附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的 n 元。于是,他把每件物品规定了一个重要度,分为 5 等:用整数 1 - 5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 n 元的前提下,使每件物品的价格与重要度的乘积的总和最大。
设第 j件物品的价格为 v_j,重要度为w_j,共选中了 k 件物品,编号依次为 j_1,j_2,......,j_k,则所求的总和为:
v j_1 * w j_1+vj_2 * w_j_2+...... +vj_k * wj_k。
请你帮助金明设计一个满足要求的购物单。
输入格式
第一行有两个整数,分别表示总钱数 n 和希望购买的物品个数 m。
第 2 到第 (m + 1) 行,每行三个整数,第 (i + 1) 行的整数 v_i,p_i,q_i 分别表示第 i 件物品的价格、重要度以及它对应的的主件。如果 q_i=0,表示该物品本身是主件。
输出格式
输出一行一个整数表示答案。
样例 #1
样例输入 #1
1000 5 800 2 0 400 5 1 300 5 1 400 3 0 500 2 0
样例输出 #1
2200
提示
数据规模与约定
对于全部的测试点,保证 1 <=n <=3.2 * 10^4,1<=m <=60,0 <=v_i <=10^4,1<=p_i<=5,0<=q_i<= m,答案不超过 2*10^5。
解题思路:为多种组合的背包dp
用二维数组来存符合条件的附件
dp可分为四种情形:1只选主件
2选主件和附件1
3选主件和附件2
4选主件、附件1、附件2
完整代码如下:
#include <bits/stdc++.h> using namespace std; const int maxn=5e4+5; int n,m; int v[maxn],w[maxn],f[maxn],fw[maxn][3],fv[maxn][3],p[maxn]; int main() { cin>>n>>m; int x,y,q; for(int i=1; i<=m; i++){ cin>>x>>y>>q; if(q==0){ v[i]=x*y; w[i]=x; }else{ f[q]++; fv[q][f[q]]=x*y; fw[q][f[q]]=x; } } for(int i=1; i<=m; i++){ for(int j=n; j>=w[i]; j--){ if(j>=w[i]) p[j]=max(p[j],p[j-w[i]]+v[i]);//只选主件 if(j>=w[i]+fw[i][1]) p[j]=max(p[j],p[j-w[i]-fw[i][1]]+v[i]+fv[i][1]);//选主件和附件1 if(j>=w[i]+fw[i][2]) p[j]=max(p[j],p[j-w[i]-fw[i][2]]+v[i]+fv[i][2]);//选主件和附件2 if(j>=w[i]+fw[i][1]+fw[i][2]) p[j]=max(p[j],p[j-w[i]-fw[i][1]-fw[i][2]]+v[i]+fv[i][1]+fv[i][2]);//选主件、附件1、附件2 } } cout<<p[n]; return 0; }