前提
鄙人只是一位普通のC++学生,如有讲解不好之处,还请各位大佬指点讲解一番!
温馨提示:此篇文章只讲述程序题目,不包括GESP中的理论题。
第一题-小杨的幸运数字
题目描述
小杨认为他的幸运数字应该恰好有两种不同的质因子,例如,12=2∗2∗3 的质因子有 2,3,恰好为两种不同的质因子,因此 12 是幸运数字,而 30=2∗3∗5 的质因子有 2,3,5,不符合要求,不为幸运数字。
小杨现在有 n 个正整数,他想知道每个正整数是否是他的幸运数字。
输入
第一行包含一个正整数 n,代表正整数个数。
之后 n 行,每行一个正整数。
输出
输出 n 行,对于每个正整数,如果是幸运数字,输出 1,否则输出 0。
样例输入
3
7
12
30
样例输出
0
1
0
提示
对于 40% 的数据,保证 1≤n≤100,正整数不超过 10的5次方。
对于 100% 的数据,保证 1≤n≤104,正整数不超过 10的6次方。
解题思路
要判断每个整数是否是“小杨的幸运数字”,我们需要检查该整数的质因子数量。如果质因子数量正好是两个,则该整数是幸运数字。
-
质因子分解:
- 对于每个整数,我们需要找出它所有的质因子。质因子是指不能再分解的因子,且必须是质数。
- 例如,12 的质因子为 2 和 3,30 的质因子为 2、3 和 5。
-
统计质因子数量:
- 使用unordered_set来存储质因子,因为unordered_set可以自动处理重复的质因子,并且插入和查找的时间复杂度为常数级别,效率较高。
-
判断质因子数量:
- 统计不同质因子的数量。如果质因子的数量是 2,则该数是幸运数字;否则不是。
芝士插入:unordered_set可直译为“无序 set 容器”,即 unordered_set 容器和 set 容器很像,唯一的区别就在于 set 容器会自行对存储的数据进行排序,而 unordered_set 容器不会。
本质是使用hash散列的方式存储数据,是一种使用hash值作为key的容器,所以当有频繁的搜索、插入和移除拥有常数时间。
unordered_set存储原理是声明一个有n个桶的数据结构,计算加入到unordered_set的新的值hash,然后计算hash%n后的值x,将新的值加入到桶x中。当桶x中已经有了元素,就直接链接在后边。当数据结构中的元素满足一定数量时需要扩充桶的数量,并重新构建桶结构。(来自CSDN-云端一散仙)
代码如下:
#include <bits/stdc++.h>
using namespace std;
int f(int num) { //用于计算一个整数的不同质因子数量
unordered_set<int> set;
while (num % 2 == 0) {
set.insert(2);
num /= 2;
}
for (int i = 3; i * i <= num; i += 2) {
while (num % i == 0) {
set.insert(i);
num /= i;
}
}
if (num > 2) {
set.insert(num);
}
return set.size();
}
int main() {
int n;
cin >> n;
vector<int> numbers(n);
for (int i = 0; i < n; i++) {
cin >> numbers[i];
}
vector<int> results(n);
for (int i = 0; i < n; i++) {
int cnt = f(numbers[i]);
results[i] = (cnt == 2) ? 1 : 0;
}
for (int i = 0; i < n; i++) {
cout << results[i] << endl;
}
return 0;
}
时间复杂度:
- 质因子分解的最坏情况是 O(sqrt(n)) 次迭代,每次迭代最多需要 O(1) 的时间来更新集合,因此总时间复杂度为 O(n)。
- 对于 n 个整数,每个整数都要执行一次质因子分解,所以整体时间复杂度是 O(nsqrt(m)),其中 m 是所有整数中的最大值。
空间复杂度:
- 空间复杂度主要由unordered_set的空间消耗决定。由于质因子个数最多为 log(n),空间复杂度为 O(logm)。
- 除此之外,程序使用了固定大小的额外空间来存储结果和其他变量,总体空间复杂度为 O(n+logm)。
第二题-黑白格
题目描述
小杨有一个 n 行 m 列的网格图,其中每个格子要么是白色,要么是黑色。
小杨想知道至少包含 k 个黑色格子的最小子矩形包含了多少个格子
输入
第一行包含三个正整数 n,m,k,含义如题面所示。
之后 n 行,每行一个长度为 m 的 01 串,代表网格图第 i 行格子的颜色,如果为 0,则对应格子为白色,否则为黑色。
输出
输出一个整数,代表至少包含 k 个黑色格子的最小子矩形包含格子的数量,如果不存在则输出 0。
样例输入
4 5 5
00000
01111
00011
00011
样例输出
6
提示
对于 20% 的数据,保证 1≤n,m≤10。
对于另 40% 的数据,保证 n=1。
对于 100% 的数据,保证 1≤n,m≤100,1≤k≤n×m。
解题思路:
要解这个问题,我们需要找出一个包含至少k个黑色格子的最小子矩形的面积
-
输入读取:
- 首先,读取网格的尺寸n(行数)和m(行数),以及目标黑色格子的最小数量
k
。 - 然后,读取网格数据,并将每个格子的值存储到
v
矩阵中,其中v[i][j]代表第i行第j列的值(0或1)。
- 首先,读取网格的尺寸n(行数)和m(行数),以及目标黑色格子的最小数量
-
初始化:
- 初始化m,用来记录当前找到的最小子矩形面积,初始值设为网格中可能的最大面积。
-
遍历所有可能的横向边界:
- 外层循环通过ding变量遍历所有可能的上边界,从上边界ding到下边界di。对于每一对边界ding和di,我们将计算这段范围内每列的累计黑色格子数。
-
计算每列的累计黑色格子数:
- 内层循环更新cnt数组cnt[j]表示当前ding行到di之间,第j列的累计黑色格子数。
-
寻找符合条件的子矩形:
- 使用双指针技巧(l和r)来寻找和满足条件的横向范围。在每次更新cnt2(累计黑色格子数)时,检查它是否达到或超过
k
。 - 如果cnt2大于或等于k,计算当前子矩形的面积,并更新最小面积mina。
- 确保通过调整l(左边界)来维护子矩形内的黑色格子数在所需范围内。
- 使用双指针技巧(l和r)来寻找和满足条件的横向范围。在每次更新cnt2(累计黑色格子数)时,检查它是否达到或超过
-
输出结果:
- 如果mina的值仍为初始值n*m,表示没有找到符合条件的子矩形,输出100。
- 否则,输出找到的最小子矩形的面积mina。
代码如下:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m, k;
cin >> n >> m >> k;
vector<vector<int>> v(n, vector<int>(m));
for (int i = 0; i < n; i++) {
string s;
cin >> s;
for (int j = 0; j < m; j++) {
v[i][j] = s[j] - '0';
}
}
int mina = n * m;
// 遍历所有可能的上边界
for (int ding = 0; ding < n; ding++) {
vector<int> cnt(m, 0);
// 遍历所有可能的下边界
for (int di = ding; di < n; di++) {
for (int j = 0; j < m; ++j) {
cnt[j] += v[di][j];
}
int cnt2 = 0;
// 使用双指针技巧找到满足条件的横向范围
for (int l = 0, r = 0; r < m; r++) {
cnt2 += cnt[r];
while (cnt2 >= k) {
int area = (di - ding + 1) * (r - l + 1);
mina = min(mina, area);
cnt2 -= cnt[l++];
}
}
}
}
if (mina == n * m) {
cout << 0;
} else {
cout << mina;
}
return 0;
}