阿里国际2025届校园招聘 0826算法岗笔试
目录
- 1. 第一题
- 2. 第二题
- 3. 第三题
⏰ 时间:2024/08/26
🔄 输入输出:ACM格式
⏳ 时长:100min
本试卷分为单选,多选,编程三部分,这里只展示编程题。
1. 第一题
题目描述
小红有一个大小为 n × n n \times n n×n 的 01 矩阵,小红每次操作可以选择矩阵的一个元素进行反置。小红想知道,最少需要多少次操作,可以让矩阵变成一个单位矩阵,即只有主对角线上有 1,其他位置都是 0(即形如:
[ 1 0 ⋯ 0 0 1 ⋯ 0 ⋮ ⋮ ⋱ ⋮ 0 0 ⋯ 1 ] \begin{bmatrix} 1 & 0 & \cdots & 0 \\ 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \ddots & \vdots \\ 0 & 0 & \cdots & 1 \\ \end{bmatrix} 10⋮001⋮0⋯⋯⋱⋯00⋮1
)。请你帮助小红解决这个问题。
若当前数字为 0
,反置后为 1
;若当前数字为 1
,翻转后为 0
。
输入描述
第一行输入一个整数 n n n,表示矩阵的大小 ( 1 ≤ n ≤ 1000 ) (1 \leq n \leq 1000) (1≤n≤1000)。
此后 n n n 行,第 i i i 行输入 n n n 个整数 a i , 1 , a i , 2 , … , a i , n a_{i,1}, a_{i,2}, \dots, a_{i,n} ai,1,ai,2,…,ai,n,表示矩阵中第 i i i 行第 j j j 列的元素 ( a i , j = 0 或 1 ) (a_{i,j} = 0 \text{ 或 } 1) (ai,j=0 或 1)。
输出描述
在一行上输出一个整数,代表最少需要多少次操作。
题解
模拟即可。
#include <bits/stdc++.h>using namespace std;int main() {int N;cin >> N;int ans = 0;for (int i = 0; i < N; i++) {for (int j = 0; j < N; j++) {int x;cin >> x;if (i == j) {if (x != 1) ans++;} else {if (x != 0) ans++;}}}cout << ans << endl;return 0;
}
2. 第二题
题目描述
小红定义 f ( x ) f(x) f(x) 表示数字 x x x 的因子个数,例如 f ( 6 ) = 4 f(6) = 4 f(6)=4(6 的因子有 1 , 2 , 3 , 6 1, 2, 3, 6 1,2,3,6)。
认为一个完美三元组 ( i , j , k ) (i, j, k) (i,j,k) ( 1 ≤ i < j < k ≤ n ) (1 \leq i < j < k \leq n) (1≤i<j<k≤n) 需要满足: f ( a i ) − f ( a j ) = f ( a k ) f(a_i) - f(a_j) = f(a_k) f(ai)−f(aj)=f(ak)。
小红给你一个长度为 n n n 的数组 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,…,an,请你帮助她求出有多少个不同的完美三元组。
当完美三元组中存在在一个位置数字不同,即认为是不同的方案。例如: ( 1 , 2 , 3 ) (1, 2, 3) (1,2,3) 和 ( 1 , 2 , 4 ) (1, 2, 4) (1,2,4) 为两种方案。
输入描述
第一行输入一个整数 n n n ( 3 ≤ n ≤ 1 0 5 ) (3 \leq n \leq 10^5) (3≤n≤105),代表数组中的元素数量。
第二行输入 n n n 个数字 a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,…,an ( 1 ≤ a i ≤ 1 0 6 ) (1 \leq a_i \leq 10^6) (1≤ai≤106) 表示数组元素。
输出描述
在一行上输出一个整数,代表三元组数量。
题解
给定范围 a i ≤ 1 0 6 a_i \leq 10^6 ai≤106,可以预计算所有数字 1 1 1 到 1 0 6 10^6 106 的因子个数,这可以通过简单的整除操作来实现。具体地,对于每个整数 i i i,将其倍数的因子个数递增,即 divisor_count [ j ] + = 1 \text{divisor\_count}[j] += 1 divisor_count[j]+=1,其中 j j j 是 i i i 的倍数。在获取了所有元素的因子个数后,问题可以转化为在数组中寻找满足 f ( a i ) − f ( a j ) = f ( a k ) f(a_i) - f(a_j) = f(a_k) f(ai)−f(aj)=f(ak) 的三元组。使用两次遍历来分割问题,将当前位置 j j j 的左侧部分视为前缀,右侧部分视为后缀。分别用计数数组来记录前缀和后缀中因子个数的频率。
对于每个位置 j j j,更新右侧部分的计数,移除当前元素的计数,并遍历所有可能的 f ( a i ) f(a_i) f(ai) 值来验证是否存在满足条件的 f ( a k ) f(a_k) f(ak)。具体来说,对于每个前缀中的 f ( a i ) f(a_i) f(ai),计算 f ( a k ) = f ( a i ) − f ( a j ) f(a_k) = f(a_i) - f(a_j) f(ak)=f(ai)−f(aj),并检查后缀中是否存在该值。如果存在,将前缀中 f ( a i ) f(a_i) f(ai) 的数量与后缀中 f ( a k ) f(a_k) f(ak) 的数量相乘,并将结果加到总计数中。
#include <bits/stdc++.h>using namespace std;
using i64 = long long;const int MAX_N = 1e5 + 5;
const int MAX_A = 1e6 + 5;
const int MAX_DIVISORS = 240 + 5;int n;
vector<int> numbers;
vector<int> divisor_count(MAX_A, 0); // 约数个数表
vector<int> divisor_counts; // 输入数组中每个元素的约数个数
vector<int> prefix_counts(MAX_DIVISORS, 0); // 前缀中约数个数的计数
vector<int> suffix_counts(MAX_DIVISORS, 0); // 后缀中约数个数的计数// 预计算每个数的约数个数
void compute_divisors() {for (int i = 1; i < MAX_A; ++i) {for (int j = i; j < MAX_A; j += i) {divisor_count[j]++;}}
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);compute_divisors();cin >> n;numbers.resize(n);divisor_counts.resize(n);unordered_map<int, int> total_counts; // 统计每种约数个数的总数for (int i = 0; i < n; ++i) {cin >> numbers[i];divisor_counts[i] = divisor_count[numbers[i]];total_counts[divisor_counts[i]]++;}for (int d = 1; d < MAX_DIVISORS; ++d) {suffix_counts[d] = total_counts[d];}i64 total_triplets = 0;for (int j = 0; j < n; ++j) {int div_count_j = divisor_counts[j];suffix_counts[div_count_j]--;// 枚举可能的 div_count_ifor (int div_count_i = 1; div_count_i < MAX_DIVISORS; ++div_count_i) {if (prefix_counts[div_count_i] > 0) {int div_count_k = div_count_i - div_count_j;if (div_count_k >= 1 && div_count_k < MAX_DIVISORS) {if (suffix_counts[div_count_k] > 0) {total_triplets += (i64)prefix_counts[div_count_i] * suffix_counts[div_count_k];}}}}prefix_counts[div_count_j]++;}cout << total_triplets << endl;return 0;
}
3. 第三题
题目描述
小红有一个六边形形状的 01 字符串生成器,一共有 A , B , C , D , E , F A, B, C, D, E, F A,B,C,D,E,F 六个部分,每一个部分初始会随机生成一个状态 0 0 0 或 1 1 1,随后,按照 A , B , C , D , E , F A, B, C, D, E, F A,B,C,D,E,F 的顺序形成一个 01 字符串,然后显示在蓝色部分的屏幕上。
初始时,小红随机让生成器生成了一个字符串 S S S,但这个并不是小红喜欢的,所以她想将 S S S 变成目标串 T T T。为此,每次她可以执行以下操作之一:
- 对 A A A 的状态取反,但该操作会同步把 B B B 和 F F F 的状态取反;
- 对 B B B 的状态取反,但该操作会同步把 A A A 和 C C C 的状态取反;
- 对 C C C 的状态取反,但该操作会同步把 B B B 和 D D D 的状态取反;
- 对 D D D 的状态取反,但该操作会同步把 C C C 和 E E E 的状态取反;
- 对 E E E 的状态取反,但该操作会同步把 D D D 和 F F F 的状态取反;
- 对 F F F 的状态取反,但该操作会同步把 E E E 和 A A A 的状态取反。
小红想知道,要将 S S S 变成 T T T 至少需要执行几次操作。
注意:若当前字符为 0
,取反后为 1
;若当前字符为 1
,取反后为 0
。
输入描述
每个测试文件中包含多组测试数据。
第一行输入一个整数 T T T ( 1 ≤ T ≤ 1 0 5 ) (1 \leq T \leq 10^5) (1≤T≤105),代表数据组数,每组测试数据描述如下:
- 第一行输入一个长度为 6 6 6 且只由 0 0 0 和 1 1 1 构成的字符串 S S S,代表初始串。
- 第二行输入一个长度为 6 6 6 且只由 0 0 0 和 1 1 1 构成的字符串 T T T,代表目标串。
输出描述
对于每一组测试数据,在一行上输出一个整数,代表把 S S S 变为 T T T 的最小操作次数;如果无法变成目标串,则直接输出 -1
。
题解
考虑采用BFS的方法。BFS适合用于寻找无权图中最短路径的情况,这里每个状态都可以视为图中的一个节点,而每次操作产生的新状态则构成与当前状态的边。我们需要记录已经访问过的状态,以避免重复计算和无效循环。
在具体实现中,我们首先检查初始状态 S S S 是否已经等于目标状态 T T T,如果相等,则无需任何操作,返回 0 0 0。接下来,将初始状态入队,设定操作计数为 0 0 0,并维护一个哈希表以标记访问过的状态。然后,进行状态转移,对于当前状态的每一种操作,通过取反相应的部分生成新的状态,并检查是否已达到目标状态。如果达到了目标状态,则返回当前操作计数加一。如果未达到,且该状态未被访问,则将其入队并继续探索。
如果在遍历所有可能的状态后仍未找到目标状态,则返回 − 1 -1 −1,表示无法通过有效的操作将 S S S 转换为 T T T。
#include <bits/stdc++.h>using namespace std;vector<vector<int>> ops = {{0, 1, 5},{0, 1, 2},{1, 2, 3},{2, 3, 4},{3, 4, 5},{0, 4, 5}
};string flip(const string& s, const vector<int>& positions) {string result = s;for (int pos : positions) {result[pos] = (result[pos] == '0') ? '1' : '0';}return result;
}int bfs(const string& start, const string& target) {if (start == target) return 0;queue<pair<string, int>> q;unordered_map<string, bool> visited;q.push({start, 0});visited[start] = true;while (!q.empty()) {auto [current, steps] = q.front();q.pop();for (const auto& op : ops) {string next = flip(current, op);if (next == target) return steps + 1;if (!visited[next]) {visited[next] = true;q.push({next, steps + 1});}}}return -1;
}int main() {int T;cin >> T;while (T--) {string S, T;cin >> S >> T;cout << bfs(S, T) << endl;}return 0;
}