2024.6 GESP-5程序题

前提

鄙人只是一位普通の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次方。

解题思路

要判断每个整数是否是“小杨的幸运数字”,我们需要检查该整数的质因子数量。如果质因子数量正好是两个,则该整数是幸运数字。

  1. 质因子分解

    • 对于每个整数,我们需要找出它所有的质因子。质因子是指不能再分解的因子,且必须是质数。
    • 例如,12 的质因子为 2 和 3,30 的质因子为 2、3 和 5。
  2. 统计质因子数量

    • 使用unordered_set来存储质因子,因为unordered_set可以自动处理重复的质因子,并且插入和查找的时间复杂度为常数级别,效率较高。
  3. 判断质因子数量

    • 统计不同质因子的数量。如果质因子的数量是 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(log⁡m)。
  • 除此之外,程序使用了固定大小的额外空间来存储结果和其他变量,总体空间复杂度为 O(n+log⁡m)。

第二题-黑白格

题目描述

小杨有一个 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个黑色格子的最小子矩形的面积

  1. 输入读取:

    • 首先,读取网格的尺寸n(行数)和m(行数),以及目标黑色格子的最小数量k
    • 然后,读取网格数据,并将每个格子的值存储到v矩阵中,其中v[i][j]代表第i行第j列的值(0或1)。
  2. 初始化:

    • 初始化m,用来记录当前找到的最小子矩形面积,初始值设为网格中可能的最大面积。
  3. 遍历所有可能的横向边界:

    • 外层循环通过ding变量遍历所有可能的上边界,从上边界ding到下边界di。对于每一对边界ding和di,我们将计算这段范围内每列的累计黑色格子数。
  4. 计算每列的累计黑色格子数:

    • 内层循环更新cnt数组cnt[j]表示当前ding行到di之间,第j列的累计黑色格子数。
  5. 寻找符合条件的子矩形:

    • 使用双指针技巧(l和r)来寻找和满足条件的横向范围。在每次更新cnt2(累计黑色格子数)时,检查它是否达到或超过k
    • 如果cnt2大于或等于k,计算当前子矩形的面积,并更新最小面积mina。
    • 确保通过调整l(左边界)来维护子矩形内的黑色格子数在所需范围内。
  6. 输出结果:

    • 如果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;
}

End

  • 18
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值