【春招笔试】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. 只能选择空闲座位;
  2. 全部 个人的座位需要位于同一行,且保持连续;
  3. 对于小组选中的每一个座位,其上下左右相邻的座位要么不存在,要么为空闲,要么同样被小组成员选中。

现在,小柯需要处理多次座位查询请求。每次请求会给出小组人数 ,她需要计算有多少种不同的座位安排方案满足上述要求。两种方案被认为是不同的,当且仅当至少有一个人的座位位置不同。如果不存在任何合法方案,则返回 。由于方案数可能很大,请将答案对 取模后输出。

输入格式

第一行输入三个整数 ,表示自习室座位的行数、列数和查询次数。

接下来 行,第 行输入 个整数 ,其中 表示自习室第 行第 列的座位状态。 表示空闲, 表示已被占用。

行输入 个整数 ,表示每次查询中学习小组的人数。

输出格式

对于每次查询,在一行上连续输出一个整数,表示不同的座位安排方案数。由于答案可能很大,请将答案对 取模后输出。

样例输入

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种不同的满足条件的座位安排方案。

题解

这道题目本质上是在处理满足特定条件的座位安排问题。题目要求学习小组成员必须坐在同一行的连续座位上,而且选中座位的相邻位置要么不存在,要么空闲,要么也是被选中的位置(避免与陌生人相邻)。

我们可以分两步解决这个问题:

  1. 首先标记出"安全"的空闲座位:一个空闲座位如果它上下方要么没有座位,要么也是空闲的,我们就认为它是"纵向安全"的。

  2. 然后对每一行寻找连续的"安全"座位段。对于每个长度为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%内容,订阅专栏后可继续查看/也可单篇购买

互联网刷题笔试宝典 文章被收录于专栏

互联网刷题笔试宝典,这里涵盖了市面上大部分的笔试题合集,希望助大家春秋招一臂之力

全部评论

相关推荐

评论
1
2
分享

创作者周榜

更多
牛客网
牛客企业服务