携程笔试 携程笔试题 20260312
笔试时间:2026年3月12日
往年笔试合集:
第一题:尾巴大人
难度:简单(签到题)
题目描述
众所周知 ! 很像一个尾巴,表示 n 的阶乘即 n!。给定一个整数 n,游游想知道 n! 的个位数字是多少,请输出这个值。
输入描述
输入一行一个整数 n 表示询问值。
输出描述
输出一个整数表示 n! 的个位数字。
样例输入1
1
样例输出1
1
样例输入2
7
样例输出2
0
样例说明
7! = 5040,个位是 0。
参考题解
解题思路:
这道题的核心思路基于阶乘的数学性质:当 n >= 5 时,阶乘的展开式中必然同时包含因子 2 和 5。因为 2 × 5 = 10,这意味着任何大于等于5的整数阶乘,其计算结果的末尾至少会带有一个 0,因此它的个位数字永远是 0。
对于 n < 5 的情况,由于计算量极小,直接采用穷举的方式硬编码结果:1! = 1,2! = 2,3! = 6,4! = 24(个位取4)。
Java:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
if (scanner.hasNextLong()) {
long n = scanner.nextLong();
if (n >= 5) {
System.out.println(0);
} else if (n == 4) {
System.out.println(4);
} else if (n == 3) {
System.out.println(6);
} else if (n == 2) {
System.out.println(2);
} else if (n == 1) {
System.out.println(1);
}
}
}
}
C++:
#include <iostream>
using namespace std;
int main() {
long long n;
cin >> n;
if (n >= 5) {
cout << 0 << endl;
} else if (n == 4) {
cout << 4 << endl;
} else if (n == 3) {
cout << 6 << endl;
} else if (n == 2) {
cout << 2 << endl;
} else if (n == 1) {
cout << 1 << endl;
}
return 0;
}
Python:
n = int(input())
if n >= 5:
print(0)
elif n == 4:
print(4)
elif n == 3:
print(6)
elif n == 2:
print(2)
elif n == 1:
print(1)
第二题:拆盲盒
难度:中等
题目描述
给定一个正整数 n 与一个正整数 m。你需要把 n 拆分为若干个素数之和(可以重复选择同一个素数),设最终使用的素数个数为 k。同时要求:所选素数中奇素数(除 2 以外的素数)的数量必须是 m 的正整数倍(不能为 0)。请在满足约束的前提下,使 k 最大化,并输出这个最大的 k。若不存在任何满足要求的拆分方案,输出 -1。
【名词解释】
素数:大于 1 且只有 1 与其本身两个正因子的整数;
奇素数:除 2 以外的素数。
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1 ≤ T ≤ 10^5)表示测试数据组数,每组测试数据描述如下:在一行上输入两个整数 n, m(1 ≤ n ≤ 10^12, 1 ≤ m ≤ 10^9)。
输出描述
对于每一组测试数据,新起一行输出一个整数:满足要求的最大的素数个数 k;若无解,则输出 -1。
样例输入
4 6 2 2 1 30 4 1000000000000 1
样例输出
2 -1 13 499999999999
样例说明
对于第一组样例 6 可以拆分为 3 + 3。
参考题解
解题思路:
涉及算法:贪心算法 + 奇偶性分析。
为了使拆分出的素数总个数最大化,我们需要尽可能多地使用数值最小的素数,即偶素数 2 和奇素数 3。为了把更多的数值份额留给消耗仅为 2 的偶素数,我们应该使用刚好满足条件的最少数量的奇素数(即全选 3),并确保减去这些 3 之后,剩下的数值是一个非负偶数,以便能被 2 完全整除。
具体逻辑通过奇偶性来判断:如果总和 n 与要求的奇素数基数 m 奇偶性相同,那么选 m 个奇素数刚好能让剩余值为偶数;如果 n 是偶数而 m 是奇数,选 m 个奇素数会导致剩余值为奇数(无法全用 2 凑齐),所以必须将奇素数的要求数量翻倍到 2m;如果 n 是奇数而 m 是偶数,因为偶数个奇数相加永远是偶数,再加上若干个 2 永远凑不出奇数的 n,这种天然的奇偶冲突会导致绝对无解(输出 -1)。最后,只需判断总和 n 是否足够容纳最少数量的 3(即 minOddSum <= n / 3),如果满足,则直接用公式算出最大个数。
Java:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
if (!in.hasNextInt()) return;
int testCases = in.nextInt();
while (testCases-- > 0) {
long totalSum = in.nextLong();
long reqOddCount = in.nextLong();
long minOddSum = -1;
if (totalSum % 2 == reqOddCount % 2) {
minOddSum = reqOddCount;
} else if (totalSum % 2 == 0 && reqOddCount % 2 != 0) {
minOddSum = 2 * reqOddCount;
}
if (minOddSum != -1 && minOddSum <= totalSum / 3) {
System.out.println((totalSum - minOddSum) / 2);
} else {
System.out.println("-1");
}
}
}
}
C++:
#include <iostream>
using namespace std;
int main() {
int testCases;
cin >> testCases;
while (testCases-- > 0) {
long long totalSum, reqOddCount;
cin >> totalSum >> reqOddCount;
long long minOddSum = -1;
if (totalSum % 2 == reqOddCount % 2) {
minOddSum = reqOddCount;
} else if (totalSum % 2 == 0 && reqOddCount % 2 != 0) {
minOddSum = 2 * reqOddCount;
}
if (minOddSum != -1 && minOddSum <= totalSum / 3) {
cout << (totalSum - minOddSum) / 2 << "\n";
} else {
cout << -1 << "\n";
}
}
return 0;
}
Python:
import sys
input = sys.stdin.readline
test_cases = int(input())
for _ in range(test_cases):
total_sum, req_odd_count = map(int, input().split())
min_odd_sum = -1
if total_sum % 2 == req_odd_count % 2:
min_odd_sum = req_odd_count
elif total_sum % 2 == 0 and req_odd_count % 2 != 0:
min_odd_sum = 2 * req_odd_count
if min_odd_sum != -1 and min_odd_sum <= total_sum // 3:
print((total_sum - min_odd_sum) // 2)
else:
print(-1)
第三题:天才的数列切割
难度:较高
题目描述
给定一个长度为 n 的数组 a₁, a₂, ..., aₙ。你将对数组进行若干次"切割"操作,操作规则如下:
一开始,数组段集合仅包含整段 [1, n];
每次操作,必须从"当前数组段集合"中选择一段完整的数组段 [l, r](该段长度需严格大于 2),然后选择一个位置 mid(l ≤ mid < r),若满足:
sum(a[l..mid]) = sum(a[mid+1..r])
则可以把该数组段分成两段 [l, mid] 与 [mid+1, r],并以这两段替换原来的 [l, r],继续加入到数组段集合中;
一旦一个数组段被切割,它将从集合中移除并被两个新的子段取代。你只能选择当前集合中存在的完整段进行下一次切割,不允许跨越段边界或在段内截取任意子段进行操作。
所有区间均以原数组的下标表示,不进行重新编号;切割后得到的两个区间互不重叠,且并集为原区间。请你计算,在最优策略下,总共最多能进行多少次切割。
输入描述
第一行输入一个整数 n(1 ≤ n ≤ 10^5)表示数组的长度;第二行输入 n 个整数 a₁, a₂, ..., aₙ 表示数组中的元素。
输出描述
输出一个整数,表示最多的切割次数,若不存在任何合法的切割方案,输出 0。
样例输入
6 1 2 1 1 1 2
样例输出
2
样例说明
对全段 [1,6],可在 mid=3 处切割,因为 sum(a[1..3]) = sum(a[4..6]) = 4,得到 [1,3] 与 [4,6]。随后 [4,6] 在 mid=5 处再次切割,因为 sum(a[4..5]) = sum(a[6..6]) = 2。总共进行了 2 次切割。
参考题解
解题思路:
涉及算法:前缀和 + 二分查找 + 分治递归。
首先,代码在读取数据时预处理出了整个数组的前缀和,这样做的目的是为了在后续递归过程中,能够以 O(1) 的时间复杂度快速计算出任意子区间的元素总和。
在递归处理每次的区间 [left, right] 时,首先判断区间长度是否严格大于 2 且区间总和是否为偶数(奇数无法被平分成两个整数和),若不满足则说明无法继续切割,直接返回 0。若满足条件,则计算出将该区间平分所需达到的"目标前缀和",并利用二分查找快速定位合法的切割位置;如果找到了该切割点,就将本次操作计为 1 次切割,并累加上对切开的左右两个新子区间继续递归求解出的切割次数。
Java:
import java.io.InputStream;
import java.util.Arrays;
public class Main {
static long[] preSums;
public static void main(String[] args) throws Exception {
InputStream stream = System.in;
int ch = stream.read();
while (ch <= 32) {
if (ch == -1) return;
ch = stream.read();
}
int len = 0;
while (ch > 32) {
len = len * 10 + (ch - '0');
ch = stream.read();
}
preSums = new long[len + 1];
for (int i = 1; i <= len; i++) {
ch = stream.read();
while (ch <= 32) {
if (ch == -1) break;
ch = stream.read();
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南