【春招笔试】2025.04.26淘天春招笔试改编题
✅ 春招备战指南 ✅
💡 学习建议:
- 先尝试独立解题(建议用时:90分钟/套)
- 对照解析查漏补缺
🧸 题面描述背景等均已深度改编,做法和题目本质基本保持一致。
🍹 感谢各位朋友们的订阅,你们的支持是我们创作的最大动力
🌸 目前本专栏已经上线60+套真题改编解析,后续会持续更新的
春秋招合集 -> 互联网必备刷题宝典🔗
题目一:图书馆自习座位安排
1️⃣:标记纵向安全的空闲座位
2️⃣:统计每行连续安全座位段,并计算不同k值的合法起点数
3️⃣:乘以k!计算所有可能的排列方案数
难度:中等
这道题目的关键在于理解座位安排的约束条件,特别是不能与陌生人相邻的要求。通过标记安全座位,再统计连续段内可能的起点位置,最后乘以排列数,可以实现O(n*m + q)的高效算法。
题目二:档案管理最优排序
1️⃣:建立原序列与目标序列(升序/降序)的位置映射关系
2️⃣:分解置换循环,找出需要调整的区间
3️⃣:合并重叠区间,计算最小总成本
难度:中等
这道题目结合了置换分解与区间合并的思想。通过将排序问题转化为置换问题,可以准确找出哪些元素需要一起重排。随后通过贪心合并重叠区间,能够实现O(n log n)的最优解。
题目三:二进制信号转换器
1️⃣:分析可转换序列的性质(1的个数必须为偶数)
2️⃣:推导最少操作次数的数学公式
3️⃣:使用组合计数和快速幂计算总和
难度:中等偏难
这道题目需要深入理解二进制序列的变换性质,以及运用数学归纳和组合计数。通过发现规律,我们可以得出一个简洁的公式:(n-1) * 2^(n-2),并使用快速幂实现O(log n)的解法,有效处理大数据范围。
01. 图书馆自习座位安排
问题描述
小柯是大学图书馆的管理员,最近临近期末考试,图书馆特别开放了一个大自习室供学生们复习。自习室共有 行、
列座位,部分座位已经被占用。现在有一个学习小组想要一起复习,他们有以下座位选择要求:
- 只能选择空闲座位;
- 全部
个人的座位需要位于同一行,且保持连续;
- 对于小组选中的每一个座位,其上下左右相邻的座位要么不存在,要么为空闲,要么同样被小组成员选中。
现在,小柯需要处理多次座位查询请求。每次请求会给出小组人数 ,她需要计算有多少种不同的座位安排方案满足上述要求。两种方案被认为是不同的,当且仅当至少有一个人的座位位置不同。如果不存在任何合法方案,则返回
。由于方案数可能很大,请将答案对
取模后输出。
输入格式
第一行输入三个整数 、
、
,表示自习室座位的行数、列数和查询次数。
接下来 行,第
行输入
个整数
,其中
表示自习室第
行第
列的座位状态。
表示空闲,
表示已被占用。
第 行输入
个整数
,表示每次查询中学习小组的人数。
输出格式
对于每次查询,在一行上连续输出一个整数,表示不同的座位安排方案数。由于答案可能很大,请将答案对 取模后输出。
样例输入
1 3 4
0 0 0
0 1 2 3
2 4 3
1 1 0 1
0 0 0 0
1 3 8
4 4 2
1 0 0 0
0 0 0 0
0 0 0 1
1 1 0 1
1 2
样例输出
0 3 4 6
1 0 0
4 4
数据范围
样例1 | 第一次查询(k=0):不合法,输出0。 第二次查询(k=1):只有一个人时,可以坐在第1列、第2列或第3列,共3种方案。 第三次查询(k=2):两人可以坐在(1,2)列、(2,1)列、(2,3)列或(3,2)列,共4种方案。 第四次查询(k=3):三人有(1,2,3)列和(3,2,1)列等6种不同排列方案。 |
样例2 | 第一次查询(k=1):只有一个人时,有1种合法方案。 第二次查询(k=3):没有满足要求的方案,输出0。 第三次查询(k=8):没有满足要求的方案,输出0。 |
样例3 | 两次查询的结果都是4,表示有4种不同的满足条件的座位安排方案。 |
题解
这道题目本质上是在处理满足特定条件的座位安排问题。题目要求学习小组成员必须坐在同一行的连续座位上,而且选中座位的相邻位置要么不存在,要么空闲,要么也是被选中的位置(避免与陌生人相邻)。
我们可以分两步解决这个问题:
-
首先标记出"安全"的空闲座位:一个空闲座位如果它上下方要么没有座位,要么也是空闲的,我们就认为它是"纵向安全"的。
-
然后对每一行寻找连续的"安全"座位段。对于每个长度为len的连续段,我们需要考虑它左右两侧是否有被占用的座位,因为这会影响选座方案的合法性。
对于每个连续段和小组人数k,我们可以计算有多少个合法的起始位置。具体来说,如果连续段长度为len,那么可能的起始位置共有(len-k+1)个,但需要排除那些会导致组员与陌生人相邻的情况。
对于每次查询,我们需要统计所有行各个连续段对应人数k的合法方案数,并乘以排列数k!(因为组员之间可以互换位置,这会产生不同的方案)。
时间复杂度分析:
- 标记安全座位需要O(n*m)的时间
- 统计合法起点需要O(n*m)的时间
- 对每次查询,计算答案需要O(1)的时间
- 总时间复杂度为O(n*m + q)
对于题目给出的数据范围(n,m,q≤100),这个算法是高效的。
参考代码
- Python
import sys
input = lambda: sys.stdin.readline().strip()
# 模数常量
MOD = 998244353
def mul(a, b):
# 乘法并取模
return (a * b) % MOD
# 读取输入
n, m, q = map(int, input().split())
a = []
for i in range(n):
a.append(list(map(int, input().split())))
ks = list(map(int, input().split()))
# 标记纵向安全的空闲座位
b = [[0] * m for _ in range(n)]
for i in range(n):
for j in range(m):
# 检查座位是否空闲且上下均安全
if (a[i][j] == 0 and
(i == 0 or a[i-1][j] == 0) and
(i == n-1 or a[i+1][j] == 0)):
b[i][j] = 1
# 统计每个k的合法起点总数
seg_cnt = [0] * (m + 1)
for i in range(n):
j = 0
while j < m:
# 跳过不安全的座位
if not b[i][j]:
j += 1
continue
# 找连续安全座位段
L = j
while j < m and b[i][j]:
j += 1
R = j - 1
ln = R - L + 1
# 检查左右边界
lf_bad = 1 if (L > 0 and a[i][L-1] == 1) else 0
rt_bad = 1 if (R < m-1 and a[i][R+1] == 1) else 0
bad = lf_bad + rt_bad
# 统计不同k的方案数
for k in range(1, ln+1):
tot = ln - k + 1
cnt = tot - bad
if cnt > 0:
seg_cnt[k] = (seg_cnt[k] + cnt) % MOD
# 预计算阶乘
fact = [1] * (m + 1)
for i in range(1, m+1):
fact[i] = mul(fact[i-1], i)
# 回答查询
ans = []
for k in ks:
if 1 <= k <= m:
ans.append(mul(seg_cnt[k], fact[k]))
else:
ans.append(0)
print(" ".join(map(str, ans)))
- Cpp
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
const int MOD = 998244353;
// 计算(a*b)%MOD
int mul(ll a, ll b) {
return int((a * b) % MOD);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 读取输入
int n, m, q;
cin >> n >> m >> q;
vector<vector<int>> seats(n, vector<int>(m));
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
cin >> seats[i][j];
// 标记安全座位
vector<vector<int>> safe(n, vector<int>(m, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (seats[i][j] == 0 &&
(i == 0 || seats[i-1][j] == 0) &&
(i == n-1 || seats[i+1][j] == 0))
safe[i][j] = 1;
}
}
// 统计合法方案数
vector<ll> segment_count(m + 1, 0);
for (int i = 0; i < n; i++) {
int j = 0;
while (j < m) {
if (!safe[i][j]) {
j++;
continue;
}
// 找连续安全段
int left = j;
while (j < m && safe[i][j]) j++;
int right = j - 1;
int len = right - left + 1;
// 检查边界条件
int left_bad = (left > 0 && seats[i][left-1] == 1) ? 1 : 0;
int right_bad = (right < m-1 && seats[i][right+1] == 1) ? 1 : 0;
int bad = left_bad + right_bad;
// 计算不同k值的方案数
for (int k = 1; k <= len; k++) {
int total = len - k + 1;
int count = total - bad;
if (count > 0)
segment_count[k] = (segment_count[k] + count) % MOD;
}
}
}
// 计算阶乘
vector<int> factorial(m + 1, 1);
for (int i = 1; i <= m; i++)
factorial[i] = mul(factorial[i-1], i);
// 回答查询
for (int i = 0; i < q; i++) {
int k;
cin >> k;
if (k >= 1 && k <= m)
cout << mul(segment_count[k], factorial[k]) << ' ';
else
cout << "0 ";
}
return 0;
}
- Java
import java.io.*;
import java.util.*;
public class Main {
private static final int MOD = 998244353;
// 计算(a*b)%MOD
private static int mul(long a, long b) {
return (int)((a * b) % MOD);
}
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
// 读取输入
int n = Integer.parseInt(st.nextToken());
int m = Integer.parseInt(st.nextToken());
int q = Integer.parseInt(st.nextToken());
int[][] seats = new int[n][m];
for (int i = 0; i < n; i++) {
st = new StringTokenizer(br.readLine());
for (int j = 0; j < m; j++) {
seats[i][j] = Integer.parseInt(st.nextToken());
}
}
// 标记安全座位
int[][] safe = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (seats[i][j] == 0 &&
(i == 0 || seats[i-1][j] == 0) &&
(i == n-1 || seats[i+1][j] == 0))
safe[i][j] = 1;
}
}
// 统计合法方案数
long[] segCount = new long[m + 1];
for (int i = 0; i < n; i++) {
int j = 0;
while (j < m) {
if (safe[i][j] == 0) {
j++;
continue;
}
// 找连续安全段
int L = j;
while (j < m && safe[i][j] == 1) j++;
int R = j - 1;
int len = R - L + 1;
// 检查边界条件
int leftBad = (L > 0 && seats[i][L-1] == 1) ? 1 : 0;
int rightBad = (R < m-1 && seats[i][R+1] == 1) ? 1 : 0;
int bad = leftBad + rightBad;
// 计算不同k值的方案数
for (int k = 1; k <= len; k++) {
int total = len - k + 1;
int count = total - bad;
if (count > 0)
segCount[k] = (segCount[k] + count) % MOD;
}
}
}
// 计算阶乘
int[] fact = new int[m + 1];
fact[0] = 1;
for (int i = 1; i <= m; i++)
fact[i] = mul(fact[i-1], i);
// 回答查询
st = new StringTokenizer(br.readLine());
StringBuilder sb = new StringBuilder();
for (int i = 0; i < q; i++) {
int k = Integer.parseInt(st.nextToken());
if (k >= 1 && k <= m)
sb.append(mul(segCount[k], fact[k])).append(" ");
else
sb.append("0 ");
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力