7-1 月饼
月饼是中国人在中秋佳节时吃的一种传统食品,不同地区有许多不同风味的月饼。现给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请你计算可以获得的最大收益是多少。
注意:销售时允许取出一部分库存。样例给出的情形是这样的:假如我们有 3 种月饼,其库存量分别为 18、15、10 万吨,总售价分别为 75、72、45 亿元。如果市场的最大需求量只有 20 万吨,那么我们最大收益策略应该是卖出全部 15 万吨第 2 种月饼、以及 5 万吨第 3 种月饼,获得 72 + 45/2 = 94.5(亿元)。
输入格式:
每个输入包含一个测试用例。每个测试用例先给出一个不超过 1000 的正整数 N 表示月饼的种类数、以及不超过 500(以万吨为单位)的正整数 D 表示市场最大需求量。随后一行给出 N 个正数表示每种月饼的库存量(以万吨为单位);最后一行给出 N 个正数表示每种月饼的总售价(以亿元为单位)。数字间以空格分隔。
输出格式:
对每组测试用例,在一行中输出最大收益,以亿元为单位并精确到小数点后 2 位。
输入样例:
3 20
18 15 10
75 72 45
输出样例:
94.50
答案:
法1:
#include <iostream>
#include <string>
#include<algorithm>
#include<bits/stdc++.h>
#include<stack>
#include<set>
using namespace std;
typedef struct chan{
float l;
float mon;
float dan;
}chan;
chan a[1001];
bool cmp(chan a, chan b){//单价排序,贵的在前面
return a.dan>b.dan;
}
int main(){
int n,D;//由题意定义正整数n,最大市场需求量D
cin>>n>>D;
int i;
for(i=0;i<n;i++){ //输入n个每种月饼的库存量
cin>>a[i].l;
}
for(i=0;i<n;i++){ //输入n个每种月饼的总售价
cin>>a[i].mon;
a[i].dan=float(a[i].mon*1.0/a[i].l);//求单价
}
sort(a,a+n,cmp);
float sum=0;
i=0;
while(D>0){
if(a[i].l>=D){
sum+=D*a[i].dan;
D=0;
}
else{
sum+=a[i].l*a[i].dan;
D=D-a[i].l;
}
i++;
if(i==n){//当月饼全卖完的时候,主动退出循环。
break;
}
}
printf("%.2f",sum);
}
**思路:**先算出每种月饼的单价,按照单价从高到低排序,因为要求的是卖出月饼的最高价格,所以肯定先卖贵的,最贵的卖完卖第二贵的,然后依次循环。
最后的while循环是此题的难点,当此种月饼的库存大于需要的D时,只需要将D吨月饼全卖出去,再将D置零结束循环即可。
当当前月饼的库存小于所需要的D时,先把当前贵的月饼库存卖完,D变为D-当前库存,再用算的D和下一个月饼进行对比,知道D不为正整数即可;
此题用结构体定义会清晰很多,一个结构体就代表一种月饼,有单价,库存和总售价,如果定义数组的话需要单独的三个数组,非常不好理解和麻烦,结构体在这种情况下是最清晰的。
坑点:1、会存在月饼全卖完的情况,当D大于所有月饼之和的时候,要退出循环,也就是程序中(i==n)的时候,需要主动退出循环,否则会有段错误;
2、月饼的库存和售价都会有浮点数的可能,不要看样例给的都是int型就都设为int,需要认真读题,题目中只说了是正数,读题很重要。
法2:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct mooncake{
float num, price, unit;
};
int cmp(mooncake a, mooncake b) {
return a.unit > b.unit;
}
int main() {
int n, need;
cin >> n >> need;
vector<mooncake> a(n);
for (int i = 0; i < n; i++) scanf("%f", &a[i].num);
for (int i = 0; i < n; i++) scanf("%f", &a[i].price);
for (int i = 0; i < n; i++) a[i].unit = a[i].price / a[i].num;
sort(a.begin(), a.end(), cmp);
float result = 0.0;
for (int i = 0; i < n; i++) {
if (a[i].num <= need) {
result = result + a[i].price;
} else {
result = result + a[i].unit * need;
break;
}
need = need - a[i].num;
}
printf("%.2f",result);
return 0;
}
我个人对vector这个容器不是很理解,但是感觉跟Java中的HashMap有点相似。
附:一些vector常用函数实例解析
(1)push_back()
push_back(x) 就是在 vector 后面添加一个元素 x ,时间复杂度为 O(1)。
(2)pop_back()
pop_back(x) 用以删除 vector 的尾元素,时间复杂度为 O(1)。
(3)size()
size() 用来获得 vector 中元素的个数,时间复杂度为 O(1)。size()返回的是 unsigned 类型,不过一般来说用 %d 不会出很大问题,这一点对所有 STL 容器都是一样的。
(4)clear()
clear() 用来清空 vector 中的所有元素,时间复杂度为 O(N),其中 N 为 vector 中元素的个数。
(5)insert()
insert(it,x) 用来向 vector 的任意迭代器 it 处插入一个元素 x,时间复杂度为 O(N)。
(6)erase()
- 删除单个元素。
erase(it) 即删除迭代器为 it 处的元素。
- 删除一个区间内的所有元素。
erase(first,last) 即删除 [first,last) 内的所有元素。
7-2 会场安排问题
题目来源:王晓东《算法设计与分析》
假设要在足够多的会场里安排一批活动,并希望使用尽可能少的会场。设计一个有效的
贪心算法进行安排。(这个问题实际上是著名的图着色问题。若将每一个活动作为图的一个
顶点,不相容活动间用边相连。使相邻顶点着有不同颜色的最小着色数,相应于要找的最小
会场数。)
输入格式:
第一行有 1 个正整数k,表示有 k个待安排的活动。
接下来的 k行中,每行有 2个正整数,分别表示 k个待安排的活动开始时间和结束时间。时间
以 0 点开始的分钟计。
输出格式:
输出最少会场数。
输入样例:
5
1 23
12 28
25 35
27 80
36 50
输出样例:
在这里给出相应的输出。例如:
3
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct activity
{
int time;
bool status; //标注是活动开始时间还是结束时间,true 开始,false 结束
activity(int time, bool status) : time(time), status(status) {}
bool operator<(activity other)
{
return time < other.time;
}
};
vector<activity> v;
int solve()
{
int n = v.size();
int ans = 0;
int num = 0;
for (int i = 0; i < n; i++)
{
if (v[i].status)
{
num++; //开始,会场数增加一
}
else
{
num--; //结束,会场数减少一
}
//更新需要的会场数
if (num > ans && (i == n - 1 || v[i].time < v[i + 1].time))
{
ans = num;
}
}
return ans;
}
int main()
{
int n;
cin >> n;
int beginTime;
int endTime;
while (n--)
{
cin >> beginTime;
v.push_back(activity(beginTime, true));
cin >> endTime;
v.push_back(activity(endTime, false));
}
sort(v.begin(), v.end()); //按时间升序排序
cout << solve() << endl;
}
解题思路:
用变量num来记录需要使用的会场数
用变量ans来记录已开辟的会场数
用vector来保存时间点
对于会场的使用,就两种情况,活动开始时使用会场(num++),活动结束时回收会场(num–)
我们要找到这批活动中使用的最少会场数,则可以根据活动的先后使用时间来进行升序排序,这样就能在一系列活动的使用会场(num++)和回收会场(num–)中找到总共最少要开辟多少会场才能满足这些活动所需了
对于需要的会场数大于已开辟的会场数num>ans,则ans=num,更新开辟的会场
当满足num>ans时,如果当前时间点和下一个时间点相同时,有两种情况,下一个时间点要么是开始使用,要么是结束使用,如果是结束使用的话,则开始时间和结束时间相同,不需要使用会场,就不更新了,如果是开始时间的话,在下一轮更新开辟的会场数也是一样的(就相当于在下一轮同时开辟两个会场)
当前时间点是倒数第二个的话,最后一个时间点一定是结束时间,这时一定要更新ans(不用再判断当前时间点和下一个时间点的关系)
7-3 装箱问题
假设有N项物品,大小分别为s1s_1s1、s2s_2s2、…、sis_isi、sNs_NsN,其中sis_isi为满足1≤sis_isi≤100的整数。要把这些物品装入到容量为100的一批箱子(序号1-N)中。装箱方法是:对每项物品, 顺序扫描箱子,把该物品放入足以能够容下它的第一个箱子中。请写一个程序模拟这种装箱过程,并输出每个物品所在的箱子序号,以及放置全部物品所需的箱子数目。
输入格式:
输入第一行给出物品个数N(≤1000);第二行给出N个正整数sis_isi(1≤sis_isi≤100,表示第i项物品的大小)。
输出格式:
按照输入顺序输出每个物品的大小及其所在的箱子序号,每个物品占1行,最后一行输出所需的箱子数目。
输入样例:
8
60 70 80 90 30 40 10 20
输出样例:
60 1
70 2
80 3
90 4
30 1
40 5
10 1
20 2
5
解题思路:
#include <iostream>
#include <cstring>
using namespace std;
char box[1010];
int main()
{
memset(box,100,sizeof(box));
int N;
int t;
int num=0;
cin>>N;
int temp = N;
while(temp--)
{
cin >> t;
for (int i=0;i<N;i++)
{
int a = box[i];
if (a >= t)
{
if (a==100)
num++;
box[i] -= t;
cout << t << " " << i+1 <<endl;
break;
}
}
}
cout << num << endl;
return 0;
}
关于memset的用法,需要知道,它是以一个字节为单位批量处理,由于本题数据范围小(1000以内),故刚好可以用char 数组来批量预处理,但是,若改成int 数组再进行menset则会出错,因为int为4个字节,这样每个数组里面的数就不是100而是(100<<24)+(100<<16)+(100<<8)+100 相当于每个字节里都是100。
7-4 森森快递
森森开了一家快递公司,叫森森快递。因为公司刚刚开张,所以业务路线很简单,可以认为是一条直线上的N个城市,这些城市从左到右依次从0到(N−1)编号。由于道路限制,第i号城市(i=0,⋯,N−2)与第(i+1)号城市中间往返的运输货物重量在同一时刻不能超过CiC_iCi公斤。
公司开张后很快接到了Q张订单,其中j张订单描述了某些指定的货物要从SjS_jSj号城市运输到TjT_jTj号城市。这里我们简单地假设所有货物都有无限货源,森森会不定时地挑选其中一部分货物进行运输。安全起见,这些货物不会在中途卸货。
为了让公司整体效益更佳,森森想知道如何安排订单的运输,能使得运输的货物重量最大且符合道路的限制?要注意的是,发货时间有可能是任何时刻,所以我们安排订单的时候,必须保证共用同一条道路的所有货车的总重量不超载。例如我们安排1号城市到4号城市以及2号城市到4号城市两张订单的运输,则这两张订单的运输同时受2-3以及3-4两条道路的限制,因为两张订单的货物可能会同时在这些道路上运输。
输入格式:
输入在第一行给出两个正整数N和Q(2≤N≤10510^5105, 1≤Q≤10510^5105),表示总共的城市数以及订单数量。
第二行给出(N−1)个数,顺次表示相邻两城市间的道路允许的最大运货重量CiC_iCi(i=0,⋯,N−2)。题目保证每个CiC_iCi是不超过2312^{31}231的非负整数。
接下来Q行,每行给出一张订单的起始及终止运输城市编号。题目保证所有编号合法,并且不存在起点和终点重合的情况。
输出格式:
在一行中输出可运输货物的最大重量。
输入样例:
10 6
0 7 8 5 2 3 1 9 10
0 9
1 8
2 7
6 3
4 5
4 2
输出样例:
7
样例提示:我们选择执行最后两张订单,即把5公斤货从城市4运到城市2,并且把2公斤货从城市4运到城市5,就可以得到最大运输量7公斤。
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ll long long
const int N=2e5+5;
ll lazy[N<<2],Min[N<<2];
int n,m,a[N];
void PushDown(int rt)
{
if(!lazy[rt])return;
lazy[rt<<1]+=lazy[rt],lazy[rt<<1|1]+=lazy[rt];
Min[rt<<1]-=lazy[rt],Min[rt<<1|1]-=lazy[rt];
lazy[rt]=0;
}
void Build(int l,int r,int rt)
{
if(l==r)
{
Min[rt]=a[l];
return;
}
int mid=(l+r)>>1;
Build(lson),Build(rson);
Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
}
ll QMin(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)return Min[rt];
PushDown(rt);
int mid=(l+r)>>1;
ll ans=1e18;
if(L<=mid)ans=min(ans,QMin(L,R,lson));
if(R>mid)ans=min(ans,QMin(L,R,rson));
return ans;
}
void Update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
lazy[rt]+=c,Min[rt]-=c;
return;
}
PushDown(rt);
int mid=(l+r)>>1;
if(L<=mid)Update(L,R,c,lson);
if(R>mid)Update(L,R,c,rson);
Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
}
pair<int,int>P[N];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1; i<n; i++)scanf("%d",&a[i]);
Build(1,n-1,1);
for(int i=0,l,r; i<m; i++)
{
scanf("%d%d",&l,&r);
if(l>r)swap(l,r);
P[i]= {r,l};
}
sort(P,P+m);
ll ans=0;
for(int i=0; i<m; i++)
{
ll num=QMin(P[i].se+1,P[i].fi,1,n-1,1);
ans+=num;
Update(P[i].se+1,P[i].fi,num,1,n-1,1);
}
cout<<ans;
return 0;
}