2023年十四届省赛大学B组真题(共10道题)
算法涉及:
- 模拟: A B
- 基础算法: C(二分) G(前缀和)
- 图论: D F
- 树论: I J
- 动态规划: E
- 数据结构: H(堆+双链表)
(填) 试题 A: 日期统计
算法: 模拟
双指针
思路:
- 时间复杂度: O(365×N)
- 空间复杂度: O(N)
又是一道日期的模拟题目
我们枚举(2023年中的)每一天, 然后去100个给定的字符中顺次查找, count记录, 只有count==8才是找其了的, ans++, 输出即可
填空题: cout<<"235";即可
#include <iostream>
using namespace std;
const int N=110;
int arr[N], month[]={
0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int main() {
for(int i=1;i<=100;i++) scanf("%d", &arr[i]);
int ans=0;
for(int m=1;m<=12;m++) {
for(int d=1;d<=month[m];d++) {
string s1=m<10?"0"+to_string(m):to_string(m);
string s2=d<10?"0"+to_string(d):to_string(d);
string s="2023"+s1+s2;
int count=0, j=1;
for(int i=0;i<s.size();i++) {
for(;j<=100;j++) {
if(s[i]-'0'==arr[j]) {
count++; break;}
}
j++; // 注意++
}
if(count==8) ans++;
}
}
printf("%d", ans); // 235
return 0;
}
(填) 试题 B: 01串的熵
算法: 模拟
思路:
- 时间复杂度: O(1e8)
- 空间复杂度: O(1)
cpp中是有以2为底的对数的(log2(x))
- 公式: 依据题意去推导即可
- i为0, j为1 (因为0比1少, 所以只用枚举到n/2)
- **cmp()函数:**比较double变量是否相等的常用函数, 用于纠正误差, 只要误差小于eps, 我们就认为两数相等
填空题: cout<<"11027421";即可
#include <iostream>
#include <cmath>
using namespace std;
const double eps=1e-3;
int cmp(double a, double b) {
if(fabs(a-b)<eps) return 0;
if(a>b) return -1;
return 1;
}
int main() {
int n=23333333;
// i为0, j为1 (0比1少, 所以只用枚举到n/2)
for(int i=1;i<=n/2;i++) {
int j=n-i;
double t=(-1)*(((double)i*i/n)*log2(((double)i/n)) + ((double)j*j/n)*log2(((double)j/n)));
if(!cmp(t, 11625907.5798)) {
cout<<i<<endl;
}
}
// 11027421
return 0;
}
试题 C: 冶炼金属
算法: 二分
思路:
- **时间复杂度: O(NlogM) = 1e5 **
- 空间复杂度: O(N)
最小化二分模板
-
Vmax: 按照A/B升序, Vmax=input[0]的A/B, 因为但凡再大1, input[0]都无法满足, 所以此时最大
-
Vmin: 我们只知道Vmin∈[1, Vmax]之间, 但不知道Vmin为多少才能使得所有的input[i].A/Vmin都等于input[i].B
因为整个满足单调减, 所以二分一定可以得到答案
#include <iostream>
#include <algorithm>
#define pii pair<int,int>
using namespace std;
const int N=10010;
pii input[N];
int n, maxn, minn;
bool check(int x) {
for(int i=1;i<=n;i++) {
if(input[i].first/x!=input[i].second) return false;
}
return true;
}
int Binary_search(void) {
int l=0, r=maxn;
while(l+1!=r) {
int mid=(l+r)/2;
check(mid)?r=mid:l=mid;
}
return r;
}
int main() {
scanf("%d", &n);
for(int i=1;i<=n;i++) scanf("%d%d", &input[i].first, &input[i].second);
sort(input+1, input+1+n, [](pii a, pii b) {
return (double)a.first/a.second < (double)b.first/b.second;
});
maxn=input[1].first / input[1].second;
minn=Binary_search();
printf("%d %d",minn, maxn);
return 0;
}
试题 D: 飞机降落
算法: DFS
思路:
- 时间复杂度: O(T×2N×105) =1e9 (2s正好) [比较迷, 因为time远远小于105所以, 平均差不多1e8左右]
- 空间复杂度: O(N)
对什么进行DFS? 什么需要DFS? —> 当前最早空余时刻(time), 和当前各个飞机的降落状态(status)
因为只要我们知道time是多少, 我们就能对当前这个需要降落的飞机进行判断
-
case1: 飞机降落时刻 >当前时刻(time) 往下传t+d(飞机一到就开始降落, 往下最早空余时间就是t+l
-
case2: 当前飞机悬停l时间 > 当前时刻(time) 也可以降落, 往下传time+l, 飞机先悬着, 等到了最早空余时间, 就开始降落, 往下最早空余时间就是time+l
其他情况当前飞机就降落不了, 如果你加上最长悬停时刻都没有到我的最早空余时间, 那你怎么可能能降落, 直接跳过这个飞机,看下一个可不可以 -
注意: 代码用的状态压缩(因为N<=10), 推荐用数组, 方便理解. 问我为什么用状态压缩? 嘻嘻, 因为懒得回溯,本人喜欢压行…回溯还得占两行
#include <iostream>
using namespace std;
const int N=11;
int T[N], D[N], L[N], n, flag=0;
// 当前飞机的状态(2), 当前 空余的最早时刻
void DFS(int status, int time) {
if(status==(1<<n)-1) {
flag=1; return; }
else {
for(int i=n-1;i>=0;i--) {
if(!(status&(1<<i))) {
// 这架飞机还没有起飞
int t=T[i], d=D[i], l=L[i];
if(t>=time) DFS((status|(1<<i)), t+l); // case1: 飞机降落时刻 >当前时刻
else if(t+d>=time) DFS((status|(1<<i)), time+l); // cas