Week8

疯狂的采药

题目背景

此题为纪念 LiYuxiang 而生。

题目描述

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

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

此题和原题的不同点:

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

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

输入格式

输入第一行有两个整数,分别代表总共能够用来采药的时间 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;
}

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值