4月21日 9:00 - 13:00 B组
看不到测试结果, 没有罚时, 但是看算法优化程度
两道填空 [5’ * 2]
六道大题 [10’ * 2 + 15’ * 2 + 20’ * 2]
根据数字范围确定算法
算法模版
考纲
1. 枚举 [1-3]
2. 排序
冒泡 [2]
选择 [3]
void selection_sort(int a[], int len)
{
int i, j, min;
for (i = 0; i < len - 1; i++)
{
min = i;
for (j = i + 1; j < len; j++)
if (arr[min] > arr[j])
min = j;
swap(arr[i], arr[min]);
}
}
插入 [3]
归并 [4-5]
void merge(int *a, int l, int r, int mid)
{
int temp[r - l], tempnum = 0;
int pos1 = l, pos2 = mid + 1, i;
while (pos1 <= mid && pos2 <= r)
{
if (a[pos1] <= a[pos2])
temp[tempnum++] = a[pos1++];
else
temp[tempnum++] = a[pos2++];
}
while (pos1 <= mid)
temp[tempnum++] = a[pos1++];
while (pos2 <= r)
temp[tempnum++] = a[pos2++];
for (i = 0; i < tempnum; i++)
a[l + i] = temp[i];
}
void merge_sort(int *a, int l, int r)
{ // 排序a数组中[l,r]区间内的数为升序
if (l < r)
{
int mid = (l + r) / 2;
merge_sort(a, l, mid);
merge_sort(a, mid + 1, r);
merge(a, l, r, mid);
}
}
快速 [4-5]
void quick_sort(int *a, int l, int r)
{
if (l >= r)
return;
int key = a[l], left = l, right = r;
while (left < right)
{
while (a[right] >= key && left < right)
right--;
while (a[left] <= key && left < right)
left++;
if (left < right)
{
int t = a[left];
a[left] = a[right];
a[right] = t;
}
}
a[l] = a[left];
a[left] = key;
quick_sort(a, l, left - 1);
quick_sort(a, left + 1, r);
}
桶 [4]
堆 [4]
const int maxn = 100001;
class myheap_max
{
private:
int heap[maxn];
int heap_size;
public:
void init()
{
for (int i = 0; i < maxn; i++)
heap[i] = 0;
heap_size = 0;
}
void clear()
{
for (int i = 0; i < heap_size; i++)
heap[i] = 0;
heap_size = 0;
}
void myswap(int *t1, int *t2) { int temp = *t1, *t1 = *t2, *t2 = temp; } // 交换函数,相当于swap
int parent(int pos) { return (pos - 1) / 2; } // 获得pos结点的父亲节点
int left_children(int pos) { return pos * 2 + 1; } // 获得pos结点的左子节点
int right_children(int pos) { return pos * 2 + 2; } // 获得pos结点的右子节点
int size() { return heap_size; }
bool empty() { return heap_size == 0; }
void push(int t)
{
int now = heap_size++;
do
{
int pa = parent(now);
if (heap[pa] >= t)
break;
heap[now] = heap[pa];
now = pa;
} while (now);
heap[now] = t;
}
int top()
{
return heap[0];
}
// 调整使pos结点下的子树成为一个大根堆
void adjust_max(int pos)
{
int l = left_children(pos), r = right_children(pos), largest;
if (l < heap_size && heap[l] > heap[pos])
largest = l;
else
largest = pos;
if (r < heap_size && heap[r] > heap[largest])
largest = r;
if (largest != pos)
{
myswap(heap + largest, heap + pos);
adjust_max(largest);
}
}
void pop()
{ // pop操作
heap[0] = heap[--heap_size];
adjust_max(0);
}
};
void heap_sort(int *a, int l, int r)
{
int i;
myheap_max que;
que.init();
for (i = l; i <= r; i++)
que.push(a[i]);
for (i = r; i >= l; i--)
{
a[i] = que.top();
que.pop();
}
}
基数 [4-5]
##include <stdio.h>
const int k = 10; // 关键码区间大小
// 排序a数组区间[l,r]内的数字
void radix_sort(int *a, int l, int r, int key)
{
int i;
if (key < 1 || r - l + 1 < 2)
return;
int count[k]; // 这里count[i]存的是输入数据中i出现的次数
for (i = 0; i < k; i++)
count[i] = 0;
for (i = l; i <= r; i++)
count[a[i] / key % 10]++;
for (i = 1; i < k; i++)
count[i] += count[i - 1];
int tcount[k]; // count复制数组
for (i = 0; i < k; i++)
tcount[i] = count[i];
int temp[r - l]; // 暂存排序结果
for (i = l; i <= r; i++)
temp[--tcount[a[i] / key % 10]] = a[i]; // 一种计数排序的思维
for (i = 0; i < r - l + 1; i++)
a[l + i] = temp[i]; // 上传排序结果到a
// 对关键码0-9分出的区间分别进行基数排序
radix_sort(a, 0, count[0] - 1, key / 10);
for (i = 1; i < k; i++)
radix_sort(a, count[i - 1], count[i] - 1, key / 10);
}
int main()
{
int a[10] = {13, 55, 25, 36, 66, 38, 51, 29, 114, 157};
int key = 1, i;
for (i = 0; i < 10; i++)
while (key < a[i])
key *= 10;
radix_sort(a, 0, 9, key / 10);
for (i = 0; i < 10; i++)
cout << a[i] << " ";
cout << endl;
return 0;
}
3. 搜索
bfs [1-5]
dfs [1-5]
剪枝 [4-6]
双向BFS [5-6]
记忆化搜索 [5]
迭代加深搜索 [5-6]
启发式搜索 [7]
4. 贪心 [1-5]
5. 模拟 [1-3]
6. 二分 [2-5]
7. dp
普通一维 [3-5]
背包 [4-6]
树形 [4-6]
状压 [5-6]
数位 [5-6]
dp的常见优化 [7]
8. 高精度 [1-5]
排序
void Sort()
{
string Max = "", tmp;
cin >> n;
for (i = 0; i < n; i++)
{
cin >> tmp;
if ((tmp > Max && tmp.size() >= Max.size() ) || Max.size() < tmp.size())
{
Max = tmp;
MaxIndex = i + 1;
}
}
cout << MaxIndex << endl << Max;
}
加法
string Add(string num1, string num2)
{
string s;
int tmp = 0, i = num1.length() - 1, j = num2.length() - 1;
while (i >= 0 || j >= 0 || tmp != 0)
{
int x = i < 0 ? 0 : num1[i] - '0';
int y = j < 0 ? 0 : num2[j] - '0';
int sum = x + y + tmp;
s.push_back('0' + sum % 10); // 插入到s字符串的第一个位置
tmp = sum / 10;
i--;
j--;
}
reverse(s.begin(), s.end());
return s;
}
乘法
string Multiply(string num1, string num2)
{
int m = num1.size(), n = num2.size(), i, j;
vector<int> ans(m + n, 0);
// 从个位数开始逐位相乘
for (i = m - 1; i >= 0; i--)
{
for (j = n - 1; j >= 0; j--)
{
int mul = (num1[i] - '0') * (num2[j] - '0');
// 乘积在 res 对应的索引位置
int p1 = i + j;
int p2 = i + j + 1;
// 叠加到 ans 上
int sum = mul + ans[p2];
ans[p2] = sum % 10;
ans[p1] += sum / 10;
}
}
// 前缀0
for (i = 0; i < ans.size() && ans[i] == 0;)
i++;
// 结果转化成字符串
string s;
for (; i < ans.size(); i++)
s.push_back('0' + ans[i]);
return s.size() == 0 ? "0" : s;
}
9. 数据结构
栈 [2-4]
队列 [2-5]
链表 [2-5]
ST表 [5-6]
堆 [5-6]
树状数组 [5-6]
线段树 [6-7]
Trie 树 [5-7]
并查集 [5-6]
x表示待操作的数,p[x]表示了x的祖先。而find函数就是为了找到x的祖先。
int find(int x) // 寻找x的祖先+路径压缩
{
if (p[x] != x)
p[x] = find(p[x]);
return p[x];
}
平衡树(利用系统自带的标准库实现简单平衡树) [5-7]
10. 数学
初等数论 [3-5]
排列组合 [5-6]
二项式定理 [6]
容斥原理 [6-7]
模意义下的逆元 [5]
矩阵运算 [6-7]
高斯消元 [7]
11. 字符串
哈希 [4-5]
kmp [4-6]
马拉车 [4-6]
12. 图论
欧拉回路 [5-7]
最小生成树 [5-7]
单源最短路及差分约東系统 [5-7]
拓扑序列 [5-7]
二分图匹配 [7]
图的连通性问题#### 割点、桥、强连通分量 [7]
DFS序 [5-7]
LCA [5-7]
13. 计算几何
基础计算和基本位置关系判定 [6-7]
真题
23年省赛B
A日期统计
小蓝现在有一个长度为 100 100100 的数组,数组中的每个元素的值都在 0 00 到 9 99 的范围之内。数组中的元素从左至右如下所示:
5 55 6 66 8 88 6 66 9 99 1 11 6 66 1 11 2 22 4 44 9 99 1 11 9 99 8 88 2 22 3 33 6 66 4 44 7 77 7 77 5 55 9 99 5 55 0 00 3 33 8 88 7 77 5 55 8 88 1 11 5 55 8 88 6 66 1 11 8 88 3 33 0 00 3 33 7 77 9 99 2 22 7 77 0 00 5 55 8 88 8 88 5 55 7 77 0 00 9 99 9 99 1 11 9 99 4 44 4 44 6 66 8 88 6 66 3 33 3 33 8 88 5 55 1 11 6 66 3 33 4 44 6 66 7 77 0 00 7 77 8 88 2 22 7 77 6 66 8 88 9 99 5 55 6 66 5 55 6 66 1 11 4 44 0 00 1 11 0 00 0 00 9 99 4 44 8 88 0 00 9 99 1 11 2 22 8 88 5 55 0 00 2 22 5 55 3 33 3 33
现在他想要从这个数组中寻找一些满足以下条件的子序列:
1. 子序列的长度为 8 88;
2. 这个子序列可以按照下标顺序组成一个 yyyymmddyyyymmdd 格式的日期,并且要求这个日期是 2023 年中的某一天的日期,例如 20230902,20231223 。yyyyyyyy 表示年份, mmmm 表示月份,dddd 表示天数,当月份或者天数的长度只有一位时需要一个前导零补充。
请你帮小蓝计算下按上述条件一共能找到多少个不同的 2023 20232023 年的日期。对于相同的日期你只需要统计一次即可。
#include <stdio.h>
int main() {
int array[100] = {
5, 6, 8, 6, 9, 1, 6, 1, 2, 4, 9, 1, 9, 8, 2, 3, 6, 4, 7, 7,
5, 9, 5, 0, 3, 8, 7, 5, 8, 1, 5, 8, 6, 1, 8, 3, 0, 3, 7, 9,
2, 7, 0, 5, 8, 8, 5, 7, 0, 9, 9, 1, 9, 4, 4, 6, 8, 6, 3, 3,
8, 5, 1, 6, 3, 4, 6, 7, 0, 7, 8, 2, 7, 6, 8, 9, 5, 6, 5, 6,
1, 4, 0, 1, 0, 0, 9, 4, 8, 0, 9, 1, 2, 8, 5, 0, 2, 5, 3, 3
};
int daysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int ans = 0;
for (int month = 1; month <= 12; ++month) {
for (int day = 1; day <= daysInMonth[month]; ++day) {
int dateSeq[8] = {2, 0, 2, 3, month / 10, month % 10, day / 10, day % 10};
int k = 0;
for (int i = 0; i < 100; ++i) {
if (array[i] == dateSeq[k]) {
++k;
if (k == 8) {
ans++;
break;
}
}
}
}
}
printf("%d\n", ans);
return 0;
}
B01串的熵
对于一个长度为n的01串8=212283•••2n,香农信息熵的定义为H(S)=-Z/ P(aci) log2 (P(ai)),其中p(O),P(1)表示在这个 01 串中0和1出现的占比。比如,对于长度为23333333的01串,如果其信息熵为 11625907.5798,且0出现次数比1少,那么这个01 串中 0 出现了多少次?
//首先要理解题目的意思
//不要被题目的数据吓到
//例如当S等于100时,
// 100的信息熵 =
// -0的个数*(0的个数/总位数)*log2(0的个数/总位数)-1的个数*(1的个数/总位数)*log2(1的个数/总位数)
// 然后我们 长度为23333333的01串 从0的个数为0开始枚举,直到1.0*23333333/2
// (因为0的个数比1的个数少,所以一定不会超过23333333的一半)
// 注意点:在判断浮点数是否等于一个数的时候不能if(x == y)
// 而是要判断它是否属于某一范围,或者二者差的绝对值属于某一范围一般取<0.01
#include<stdio.h>
#include<math.h>
int main(){
double n = 23333333,sum = 0;
int o = 0,l = 0;
for(o = 0;o <= n/2;o++){
sum = 0;
sum -= o*(o / n) * log2(o / n) + (n - o)*((n - o) / n) * log2((n - o) / n);
if(sum > 11625907.5 && sum < 11625907.6){
printf("%d",o);
break;
}
}
return 0;
}
C冶炼金属
模拟
void solve()
{
int i, j, n, a, b, Min = INF, Max = -1;
cin >> n;
while (n--)
{
cin >> a >> b;
Min = min(Min, a / b);
Max = max(Max, a / (b + 1) + 1);
}
cout << Max << " " << Min << endl;
}
D飞机降落
N 架飞机准备降落到某个只有一条跑道的机场。其中第 i 架飞机在 Ti 时刻到达机场上空,到达时它的剩余油料还可以继续盘旋 Di 个单位时间,即它最早
可以于 Ti 时刻开始降落,最晚可以于 Ti + Di 时刻开始降落。降落过程需要 Li个单位时间。
一架飞机降落完毕时,另一架飞机可以立即在同一时刻开始降落,但是不能在前一架飞机完成降落前开始降落。
请你判断 N 架飞机是否可以全部安全降落。
状压dp
#include <bits/stdc++.h>
using namespace std;
typedef struct ss
{
int T, D, L;
} down;
int n, vis[20], now;
bool flag;
down pos[20];
void dfs(int step)
{
if (flag)
return;
if (step == n)
{
flag = true;
return;
}
for (int i = 1; i <= n; i++)
{
if (!vis[i])
{
if (now > pos[i].T)
{
if (now > pos[i].T + pos[i].D)
{
return;
}
else
{
vis[i] = 1;
now += pos[i].L;
dfs(step + 1);
vis[i] = 0;
now -= pos[i].L;
}
}
else
{
int pre = now;
now = pos[i].L + pos[i].T;
vis[i] = 1;
dfs(step + 1);
vis[i] = 0;
now = pre;
}
}
}
}
void solve()
{
cin >> n;
int i;
for (i = 1; i <= n; i++)
{
cin >> pos[i].T >> pos[i].D >> pos[i].L;
}
memset(vis, 0, sizeof(vis));
now = 0;
flag = false;
dfs(0);
if (flag)
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
int main()
{
int t;
cin >> t;
while (t--)
{
solve();
}
}
E接龙数列
对于一个长度为 K 的整数数列:A1, A2, . . . , AK,我们称之为接龙数列当且仅当 Ai 的首位数字恰好等于 Ai−1 的末位数字 (2 ≤ i ≤ K)。
例如 12, 23, 35, 56, 61, 11 是接龙数列;12, 23, 34, 56 不是接龙数列,因为 56的首位数字不等于 34 的末位数字。所有长度为 1 的整数数列都是接龙数列。
现在给定一个长度为 N 的数列 A1, A2, . . . , AN,请你计算最少从中删除多少个数,可以使剩下的序列是接龙序列?
dp
void solve()
{
cin >> n;
int ans = -1;
for (i = 0; i < n; i++)
{
cin >> s;
last = s[s.length() - 1];
dp[last] = max(dp[last], dp[s[0]] + 1);
}
for (i = '1'; i < '9'; i++)
ans = max(ans, dp[i]);
cout << n - ans << endl;
}
F岛屿个数
小蓝得到了一副大小为 M × N 的格子地图,可以将其视作一个只包含字符‘0’(代表海水)和 ‘1’(代表陆地)的二维数组,地图之外可以视作全部是海水,每个岛屿由在上/下/左/右四个方向上相邻的 ‘1’ 相连接而形成。
在岛屿 A 所占据的格子中,如果可以从中选出 k 个不同的格子,使得他们的坐标能够组成一个这样的排列:(x0, y0),(x1, y1), . . . ,(xk−1, yk−1),其中(x(i+1)%k , y(i+1)%k) 是由 (xi , yi) 通过上/下/左/右移动一次得来的 (0 ≤ i ≤ k − 1),
此时这 k 个格子就构成了一个 “环”。如果另一个岛屿 B 所占据的格子全部位于这个 “环” 内部,此时我们将岛屿 B 视作是岛屿 A 的子岛屿。若 B 是 A 的子岛屿,C 又是 B 的子岛屿,那 C 也是 A 的子岛屿。
请问这个地图上共有多少个岛屿?在进行统计时不需要统计子岛屿的数目。
BFS
G子串简写
对于一个字符串,只保留首尾字符,将首尾字符之间的所有字符用这部分的长度代替。例如 internation-alization 简写成 i18n,Kubernetes (注意连字符不是字符串的一部分)简写成 K8s, Lanqiao 简写成 L5o 等。
在本题中,我们规定长度大于等于 K 的字符串都可以采用这种简写方法(长度小于 K 的字符串不配使用这种简写)。
给定一个字符串 S 和两个字符 c1 和 c2,请你计算 S 有多少个以 c1 开头c2 结尾的子串可以采用这种简写?
dp
void solve()
{
int i, j, n;
string s;
char a, b;
cin >> n >> s >> a >> b;
for (i = 0; i < s.length(); i++)
{
if (s[i] == a)
dp[i] = 1;
dp[i] += dp[i - 1];
}
int ans = 0;
while (i-- >= n)
{
if (s[i] == b)
ans += dp[i - n + 1];
}
cout << ans << endl;
}
H整数删除
给定一个长度为 N 的整数数列:A1, A2, . . . , AN。你要重复以下操作 K 次:
每次选择数列中最小的整数(如果最小值不止一个,选择最靠前的),将其删除。并把与它相邻的整数加上被删除的数值。输出 K 次操作后的序列。
并查集
I景区导游
某景区一共有 N 个景点,编号1到N。景点之间共有 N -1 条双向的摆渡车线路相连,形成一棵树状结构。在景点之间往返只能通过这些摆渡车进行,需要花费一定的时间。
小明是这个景区的资深导游,他每天都要按固定顺序带客人游览其中K 个景点:A1,A2,.,AK。
今天由于时间原因,小明决定跳过其中一个景点,只带游客按顺序游览其中K-1个景点。
具体来说,如果小明选择跳过 Ai,那么他会按顺序带游客游览AL,A2,…,Ai-I,Ait1,…,Ak, (I≤i≤K)。
请你对任意一个 Ai,计算如果跳过这个景点,小明需要花费多少时间在景点之间的摆渡车上?
LCA
22年国赛B
C卡牌
这天,小明在整理他的卡牌。
他一共有 n 种卡牌,第 i 种卡牌上印有正整数数 i(i ∈ [1, n]),且第 i 种卡牌 现有 ai 张。
而如果有 n 张卡牌,其中每种卡牌各一张,那么这 n 张卡牌可以被称为一 套牌。小明为了凑出尽可能多套牌,拿出了 m 张空白牌,他可以在上面写上数 i,将其当做第 i 种牌来凑出套牌。然而小明觉得手写的牌不太美观,决定第 i 种牌最多手写 bi 张。
请问小明最多能凑出多少套牌?
22B
C刷题统计
小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 a 道题目,周六和周日每天做 b 道题目。请你帮小明计算,按照计划他将在第几天实现做题数大于等于 n 题?
模拟
void solve()
{
int i, j, a, b, n;
cin >> a >> b >> n;
int week = n / (a * 5 + b * 2), t = 0;
n = n - (a * 5 + b * 2) * week;
for (i = 1; i <= 7; i++)
{
if (n>0)
{
if (i <= 5)
{
n -= a;
}
else
n -= b;
t++;
}
}
cout << week * 7 + t << endl;
}
D修剪灌木
爱丽丝要完成一项修剪灌木的工作。有 N 棵灌木整齐的从左到右排成一排。爱丽丝在每天傍晚会修剪一棵灌木,让灌木的高度变为 0 厘米。爱丽丝修剪灌木的顺序是从最左侧的灌木开始,每天向右修剪一棵灌木。当修剪了最右侧的灌木后,她会调转方向,下一天开始向左修剪灌木。直到修剪了最左的灌木后再次调转方向。然后如此循环往复。灌木每天从早上到傍晚会长高 1 厘米,而其余时间不会长高。在第一天的早晨,所有灌木的高度都是 0 厘米。爱丽丝想知道每棵灌木最高长到多高。
void solve()
{
int i, j, n;
cin >> n;
for (i = 1; i <= n;i++)
{
cout << max((i - 1) * 2, (n - i + 1) * 2 - 2) << endl;
}
}
EX进制减法
进制规定了数字在数位上逢几进一。
X 进制是一种很神奇的进制,因为其每一数位的进制并不固定!例如说某种 X 进制数,最低数位为二进制,第二数位为十进制,第三数位为八进制,则 X 进制数 321 转换为十进制数为 65。
现在有两个 X 进制表示的整数 A 和 B,但是其具体每一数位的进制还不确定,只知道 A 和 B 是同一进制规则,且每一数位最高为 N 进制,最低为二进制。请你算出 A − B 的结果最小可能是多少。
请注意,你需要保证 A 和 B 在 X 进制下都是合法的,即每一数位上的数字要小于其进制。
大数字加减法
void solve()
{
int n, ma, mb, i;
cin >> n;
cin >> ma;
for (int i = ma; i; i--)
cin >> a[i];
cin >> mb;
for (i = mb; i; i--)
cin >> b[i];
for (i = max(ma, mb); i; i--) // 处理进制
c[i] = max(a[i], b[i]) + 1;
for (int i = max(ma, mb); i; i--) // 最低进制是二进制
c[i] = c[i] > 2 ? c[i] : 2;
d[1] = 1; // 处理每一位的价值
for (i = 2; i <= max(ma, mb); i++)
d[i] = (d[i - 1] * c[i - 1]) % MOD;
long long ka = 0, kb = 0; // 统计价值
for (i = ma; i; i--)
ka += a[i] * d[i], ka %= MOD;
for (i = mb; i; i--)
kb += b[i] * d[i], kb %= MOD;
cout << (ka - kb + MOD) % MOD; // 特别注意:模意义下的减法
}
谷:P1518,P1781,P1217,P2387,P3406,P3375,P7072,P1115,P1048,P1616,P4017,P1049,P1999(100即可),P1246,P1100,P1090,P1478,P1024,P1219,P1019,P1996,P1160,P4387,P1443,P1141,P1536
力扣:1,11,14,287,410
这个单子上知识点都搞明白了,CB省一应该是稳的(还有好多知识点我给了好几个题)