蓝桥杯C++大学B组一个月冲刺记录2024/3/7
规则:每天三题
1.鱼塘钓鱼
有 N
个鱼塘排成一排,每个鱼塘中有一定数量的鱼,例如:N=5
时,如下表:
鱼塘编号 1 2 3 4 5
第1分钟能钓到的鱼的数量(1…1000) 10 14 20 16 9
每钓鱼1分钟钓鱼数的减少量(1…100) 2 4 6 5 3
当前鱼塘到下一个相邻鱼塘需要的时间(单位:分钟) 3 5 4 4
即:在第 1个鱼塘中钓鱼第 1分钟内可钓到 10条鱼,第 2分钟内只能钓到 8 条鱼,……,第 5
分钟以后再也钓不到鱼了。
从第 1个鱼塘到第 2个鱼塘需要 3分钟,从第 2个鱼塘到第 3个鱼塘需要 5分钟,……
给出一个截止时间 T,设计一个钓鱼方案,从第 1个鱼塘出发,希望能钓到最多的鱼。
假设能钓到鱼的数量仅和已钓鱼的次数有关,且每次钓鱼的时间都是整数分钟。
多路归并
这样思考:当仅考虑前n个池塘的话,可以用总时间减去到达第n个池塘用的时间。剩下的时间K,就是将前n个池塘能获得的鱼的数量降序排列,然后取前K项和,就是答案
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int M = 105;
int a[M],b[M],st[M],spend[M];
int cmp(int i){
return max(0 , a[i] - spend[i] * b[i]);
}
int handle(int n,int T){
memset(spend,0,sizeof(spend));
int ans = 0,t = 1;
for(int i = 1;i <= T; ++i){
for(int j = 1;j <= n; ++j){
if(cmp(j) > cmp(t)) t = j;
}
ans += cmp(t);
spend[t] ++;
}
return ans;
}
int main(){
int n,T;
cin >> n;
for(int i = 1;i <= n; ++i) cin >> a[i];
for(int i = 1;i <= n; ++i) cin >> b[i];
for(int i = 2;i <= n; ++i){
cin >> st[i];
st[i] += st[i-1];
}
cin >> T;
int ans = 0;
for(int i = 1;i <= n;++i){
ans = max(ans,handle(i,T - st[i]));
}
cout << ans << endl;
return 0;
}
2.谦虚数字
给定一个包含 K个不同质数的集合 S={p1,p2,…,pK}。如果一个数的质因子全部属于集合 S,那么我们就称这个数为谦虚数字。
例如,p1、p1p2、p1p1、p1p2p3…这些数字都是谦虚数字。现在给定整数 K
和集合 S,请你求出从小到大第 N个谦虚数字是多少。
注意,我们规定 1不是谦虚数字。
丑数规律 + 多路归并
一直没找到规律,按照一般多路归并的思路还是看不出规律。
然后有帖子说和丑数那个题一样,就去看了,然后按照丑数的规律就好做了
(由于当n == 1的情况,最小堆的初始化需要特判,第一次交被卡了两组数组,直接在主函数特判了,属于偷懒行为,不可取)
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
using namespace std;
const int M = 1e5+10;
typedef pair<int,int> PII;
int a[M],res[M],p[M];
int n,T;
void merge(int k){
int cnt = 0;
res[0] = 1;
for(int i = 1;i <= n;++i){
p[a[i]] = 0;
}
priority_queue<PII,vector<PII>,greater<PII>>q;
for(int i = 1;i <= n;++i){
q.push({res[p[a[i]]] * a[i],a[i]});
}
while(cnt <= k){
auto t = q.top();
q.pop();
q.push({res[p[t.second]] * t.second , t.second});
p[t.second]++;
if(res[cnt] != t.first){
cnt++;
res[cnt] = t.first;
}
}
}
int main(){
cin >> n >> T;
for(int i = 1;i <= n; ++ i){
cin >> a[i];
}
if(n == 1){
cout << (int)pow(a[1],T) << endl;
return 0;
}
merge(T);
cout << res[T] << endl;
return 0;
}
3.序列
给定 m个序列,每个包含 n个非负整数。
现在我们可以从每个序列中选择一个数字以形成具有 m个整数的序列。
很明显,我们一共可以得到 nm个这种序列,然后我们可以计算每个序列中的数字之和,并得到 nm个值。
现在请你求出这些序列和之中最小的 n个值。
多路归并
这样子思考::当问题简化为两个数组,两个数组任取两个数求和,求最小值。当a数组有序。可分组:b[ i ] + (a[ 1 ], a[ 2 ], a[ 3 ], a[ 4 ],a[ 5 ] … ,a[ 6 ])这个序列也是有序的。所以这个问题就可以看作是多个有序数组,合并成一个有序数组(多路归并)
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int M = 2050;
typedef pair<int,int> PII;
int a[M],b[M],c[M];
int n,m,T;
void merge(){
priority_queue<PII, vector<PII>, greater<PII>>q;
for(int i = 0;i < n; ++i){
q.push({a[0] + b[i], 0});
}
for(int i = 0;i < n; ++i){
auto t = q.top();
q.pop();
c[i] = t.first;
q.push({t.first - a[t.second] + a[t.second + 1],t.second + 1});
}
for(int i = 0;i < n; ++i) a[i] = c[i];
}
int main(){
cin >> T;
while(T--){
cin >> m >> n;
for(int i = 0;i < n; ++i) cin >> a[i];
sort(a,a+n);
for(int j = 1;j <= m-1; ++j){
for(int i = 0;i < n; ++i) cin >> b[i];
merge();
}
for(int i = 0;i < n;++i) cout << a[i] << ' ';
cout << endl;
}
return 0;
}