货物摆放
题目描述
小蓝有一个超大的仓库,可以摆放很多货物。
现在,小蓝有 nn 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。
小蓝希望所有的货物最终摆成一个大的长方体。即在长、宽、高的方向上分别堆 LL、WW、HH 的货物,满足 n=L×W×Hn=L×W×H。
给定 nn,请问有多少种堆放货物的方案满足要求。
例如,当 n=4n=4 时,有以下 66 种方案:1×1×4、1×2×2、1×4×1、2×1×2、2×2×1、4×1×11×1×4、1×2×2、1×4×1、2×1×2、2×2×1、4×1×1。
请问,当 n=2021041820210418n=2021041820210418 (注意有 1616 位数字)时,总共有多少种方案?
提示:建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
long n = 2021041820210418L;
long cnt=0;
ArrayList<Long> arr = new ArrayList<>();
for(long i = 1; i < Math.sqrt(n); i++){
if ( n % i == 0 ){
arr.add(i); //如果能被整除,就放到arr数组中
long num = n / i;
if ( num != i ){
arr.add(num); //将较大的因子放入arr数组
}
}
}
for(long i:arr){
for(long j:arr){
for(long k:arr){
if(i*j*k==n) cnt++;
}
}
}
System.out.println(cnt);
scan.close();
}
}
回路计数
题目描述
蓝桥学院由 2121 栋教学楼组成,教学楼编号 11 到 2121。对于两栋教学楼 aa 和 bb,当 aa 和 bb 互质时,aa 和 bb 之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。
小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),请问他有多少种不同的访问方案?
两个访问方案不同是指存在某个 ii,小蓝在两个访问方法中访问完教学楼 ii 后访问了不同的教学楼。
提示:建议使用计算机编程解决问题。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
package lanqiao;
import java.util.Scanner;
public class lq7 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = 21; // 教学楼数量
// 预处理互质关系
boolean[][] isCoprime = new boolean[n + 1][n + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
isCoprime[i][j] = (gcd(i, j) == 1);
}
}
// 动态规划数组
long[][] dp = new long[1 << n][n];
dp[1][0] = 1; // 初始状态:第 1 栋教学楼已访问
// 动态规划
for (int mask = 1; mask < (1 << n); mask++) {
for (int i = 0; i < n; i++) {
if ((mask & (1 << i)) != 0) { // 如果当前教学楼已访问
for (int j = 0; j < n; j++) {
if ((mask & (1 << j)) == 0 && isCoprime[i + 1][j + 1]) { // 如果下一栋教学楼未访问且与当前教学楼互质
dp[mask | (1 << j)][j] += dp[mask][i]; // 更新状态
}
}
}
}
}
// 计算结果
long result = 0;
for (int i = 1; i < n; i++) { // 遍历所有可能的终点
if (isCoprime[i + 1][1]) { // 如果终点与起点互质
result += dp[(1 << n) - 1][i]; // 累加结果
}
}
System.out.println(result); // 输出结果
scan.close();
}
// 迭代实现的最大公约数计算
static int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
}
最少砝码
问题描述
你有一架天平。现在你要设计一套砝码,使得利用这些砝码可以称出任意 小于等于 NN 的正整数重量。
那么这套砝码最少需要包含多少个砝码?
注意砝码可以放在天平两边。
输入格式
输入包含一个正整数 N。
输出格式
输出一个整数代表答案。
样例输入
7
样例输出
3
样例说明
33 个砝码重量是 1、4、61、4、6,可以称出 11 至 77的所有重量。
1=1;1=1;
2=6−42=6−4(天平一边放 66,另一边放 44);
3=4−1;3=4−1;
4=4;4=4;
5=6−1;5=6−1;
6=6;6=6;
7=1+6;7=1+6;
少于 33 个砝码不可能称出 11 至 77 的所有重量。
评测用例规模与约定
对于所有评测用例,1≤N≤10000000001≤N≤1000000000。
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
int N = scan.nextInt();
int sum = 1;
for(int i = 1; ;i++){
if(N <= sum){
System.out.println(i);
break;
}
sum = sum *3+1;
}
scan.close();
}
}
路径
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图 中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点 之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条 长度为 a 和 b 的最小公倍数的无向边相连。
例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无 向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
请计算,结点 1 和结点 2021 之间的最短路径长度是多少。
提示:建议使用计算机编程解决问题。
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int[] q = new int[2022]; // 定义数组 q,用于存储最小代价
Arrays.fill(q, Integer.MAX_VALUE); // 初始化数组 q,所有值设为最大值
q[1] = 0; // 起点代价为 0
// 动态规划计算最小代价
for (int i = 1; i <= 2020; i++) {
for (int j = i + 1; j <= 2021 && (j - i) <= 21; j++) {
q[j] = Math.min(q[j], q[i] + lcm(i, j)); // 更新 q[j] 的最小代价
}
}
System.out.println(q[2021]); // 输出 q[2021] 的值
scan.close();
}
// 计算最大公约数(GCD)
static int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
// 计算最小公倍数(LCM)
static int lcm(int a, int b) {
return a * b / gcd(a, b);
}
}
分果果
题目描述
小蓝要在自己的生日宴会上将 nn 包糖果分给 mm 个小朋友。每包糖果都要分出去,每个小朋友至少要分一包,也可以分多包。
小蓝已经提前将糖果准备好了,为了在宴会当天能把糖果分得更平均一些,小蓝要先计算好分配方案。 小蓝将糖果从 11 到 nn 编号,第 ii 包糖果重 wiwi。小朋友从 11 到 mm 编号。每个小朋友只能分到编号连续的糖果。小蓝想了很久没想出合适的分配方案使得每个小朋友分到的糖果差不多重。因此需要你帮他一起想办法。为了更好的分配糖果,他可以再买一些糖果,让某一些编号的糖果有两份。当某个编号的糖果有两份时,一个小朋友最多只能分其中的一份。
请找一个方案,使得小朋友分到的糖果的最大重量和最小重量的差最小,请输出这个差。
例如,小蓝现在有 55 包糖果,重量分别为 6,1,2,7,96,1,2,7,9,如果小蓝要分给两个小朋友,则他可以将所有糖果再买一份,两个小朋友都分到 11 至 55 包糖果,重量都是 2525,差为 00。
再如,小蓝现在有 55 包糖果,重量分别为 6,1,2,7,96,1,2,7,9,如果小蓝要分给三个小朋友,则他可以将第 3 包糖果再买一份,第一个小朋友分 11 至 33 包,第二个小朋友分 33 至 44 包,第三个小朋友分第 55 包,每个小朋友分到的重量都是 99,差为 00。
再如,小蓝现在有 55 包糖果,重量分别为 6,1,2,7,96,1,2,7,9,如果小蓝要分给四个小朋友,则他可以将第 33 包和第 55 包糖果再买一份,仍然可以每个小朋友分到的重量都是 99,差为 00。
再如,小蓝现在有 55 包糖果,重量分别为 6,1,2,7,96,1,2,7,9,如果小蓝要分给五个小朋友,则他可以将第 4 包和第 55 包糖果再买一份,第一个小朋友分第 11 至 22 包重量为 77,第二个小朋友分第 33 至 44 包重量为 99,第三个小朋友分第 44 包重量为 77,第四个和第五个小朋友都分第 55 包重量为 99。差为 22。
输入描述
输入第一行包含两个整数 nn 和 mm,分别表示糖果包数和小朋友数量。
第二行包含 nn 个整数 w1,w2,⋅⋅⋅,wnw1,w2,⋅⋅⋅,wn,表示每包糖果的重量。
输出描述
输出一个整数,表示在最优情况下小朋友分到的糖果的最大重量和最小重量的差。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt(); // 糖果的包数
int m = scan.nextInt(); // 小朋友的数量
scan.nextLine(); // 读取换行符
int[] arr = new int[n + 1]; // 前缀和数组
for (int i = 1; i <= n; i++) {
arr[i] = arr[i - 1] + scan.nextInt(); // 计算前缀和
}
int ans = Integer.MAX_VALUE; // 初始化答案为最大值
int[][][] dp = new int[m + 1][n + 1][n + 1]; // 动态规划数组
// 初始化 dp 数组
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
for (int k = 0; k <= j; k++) {
dp[i][j][k] = Integer.MAX_VALUE; // 初始化为最大值
}
}
}
dp[0][0][0] = 0; // 初始状态
// 枚举最小值
for (int min = arr[n] * 2 / m; min > 0; min--) {
for (int i = 1; i <= m; i++) { // 枚举小朋友
for (int j = 1; j <= n; j++) { // 枚举糖果
int trans2 = 0, trans3 = 0; // 两个指针
for (int k = 1; k <= j; k++) { // 枚举分配点
dp[i][j][k] = dp[i][j][k - 1]; // 初始化为前一个状态
// 使用 trans2 优化状态转移
while (trans2 < k && arr[j] - arr[trans2 + 1] >= min &&
Math.max(dp[i - 1][trans2 + 1][trans2 + 1], arr[j] - arr[trans2 + 1]) <=
Math.max(dp[i - 1][trans2][trans2], arr[j] - arr[trans2])) {
trans2++;
}
// 更新 dp[i][j][k]
if (arr[j] - arr[trans2] >= min) {
dp[i][j][k] = Math.min(dp[i][j][k], Math.max(dp[i - 1][trans2][trans2], arr[j] - arr[trans2]));
}
// 使用 trans3 优化状态转移
while (trans3 < k && arr[j] - arr[trans3 + 1] >= min &&
Math.max(dp[i - 1][k][trans3 + 1], arr[j] - arr[trans3 + 1]) <=
Math.max(dp[i - 1][k][trans3 + 1], arr[j] - arr[trans3])) {
trans3++;
}
// 更新 dp[i][j][k]
if (arr[j] - arr[trans3] >= min) {
dp[i][j][k] = Math.min(dp[i][j][k], Math.max(dp[i - 1][k][trans3], arr[j] - arr[trans3]));
}
}
}
}
// 更新答案
ans = Math.min(ans, dp[m][n][n] - min);
}
// 输出结果
System.out.println(ans);
scan.close();
}
}
双向排序
题目描述
给定序列 (a1,a2,⋅⋅⋅,an)=(1,2,⋅⋅⋅,n)(a1,a2,⋅⋅⋅,an)=(1,2,⋅⋅⋅,n),即 ai=iai=i。
小蓝将对这个序列进行 mm 次操作,每次可能是将 a1,a2,⋯,aqia1,a2,⋯,aqi 降序排列,或者将 aqi,aqi+1,⋯,anaqi,aqi+1,⋯,an 升序排列。
请求出操作完成后的序列。
输入描述
输入的第一行包含两个整数 n,mn,m,分别表示序列的长度和操作次数。
接下来 mm 行描述对序列的操作,其中第 i 行包含两个整数 pi,qipi,qi 表示操作类型和参数。当 pi=0pi=0 时,表示将 a1,a2,⋅⋅⋅,aqia1,a2,⋅⋅⋅,aqi 降序排列;当 pi=1pi=1 时,表示将 aqi,aqi+1,⋯,anaqi,aqi+1,⋯,an 升序排列。
输出描述
输出一行,包含 nn 个整数,相邻的整数之间使用一个空格分隔,表示操作完成后的序列。
输入输出样例
示例
输入
3 3
0 3
1 2
0 2
输出
3 1 2
样例说明
原数列为 (1,2,3)(1,2,3)。
第 11 步后为 (3,2,1)(3,2,1)。
第 22 步后为 (3,1,2)(3,1,2)。
第 33 步后为 (3,1,2)(3,1,2)。与第 22 步操作后相同,因为前两个数已经是降序了。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringJoiner;
public class Main {
public static void main(String[] args) throws IOException {
new Main().solution();
}
private void solution() throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String[] ss = bf.readLine().split(" ");
int n = Integer.parseInt(ss[0]), m = Integer.parseInt(ss[1]);
int p, q;
int top = 0;
PQ[] a = new PQ[m + 1];
// 每次入长度最长的操作,并把同类别的短的剔除
// 可以一次剔除两个
for (int i = 0; i < m; i++) {
ss = bf.readLine().split(" ");
p = Integer.parseInt(ss[0]);
q = Integer.parseInt(ss[1]);
if (p == 0) {
while (top != 0 && a[top].p == 0) {
q = Math.max(q, a[top--].q);
}
while (top >= 2 && a[top - 1].q <= q) {
top -= 2;
}
} else if (top != 0) {// 如果为空。升序无意义
while (top != 0 && a[top].p == 1) {
q = Math.min(q, a[top--].q);
}
while (top >= 2 && a[top - 1].q >= q) {
top -= 2;
}
}
a[++top] = new PQ(p, q);
}
int l = 1, r = n, k = n;
int[] ans = new int[n + 1];
// 从第一个操作开始遍历,把除中间一段的其余补充
for (int i = 1; i <= top; i++) {
if (a[i].p == 0) {
while (a[i].q < r && l <= r) {
ans[r--] = k--;
}
} else {
while (a[i].q > l && l <= r) {
ans[l++] = k--;
}
}
if (l > r)
break;
}
// 如果是奇数,说明最后是0操作
if (top % 2 == 1) {
while (l <= r) {
ans[l++] = k--;
}
} else {
while (l <= r) {
ans[r--] = k--;
}
}
StringJoiner sj = new StringJoiner(" ");
for (int i = 1; i <= n; i++) {
sj.add(ans[i] + "");
}
System.out.println(sj);
}
class PQ {
int p, q;
PQ(int p, int q) {
this.p = p;
this.q = q;
}
}
}
左孩子右兄弟
题目描述
对于一棵多叉树,我们可以通过 “左孩子右兄弟” 表示法,将其转化成一棵二叉树。
如果我们认为每个结点的子结点是无序的,那么得到的二叉树可能不唯一。
换句话说,每个结点可以选任意子结点作为左孩子,并按任意顺序连接右兄弟。
给定一棵包含 NN 个结点的多叉树,结点从 11 至 NN 编号,其中 11 号结点是根,每个结点的父结点的编号比自己的编号小。
请你计算其通过 “左孩子右兄弟” 表示法转化成的二叉树,高度最高是多少。
注:只有根结点这一个结点的树高度为 00。
输入描述
输入的第一行包含一个整数 NN。 以下 N−1N−1 行,每行包含一个整数,依次表示 22 至 NN 号结点的父结点编号。
输出描述
输出一个整数表示答案。
输入输出样例
示例 1
输入
5
1
1
1
2
输出
4
import java.util.*;
public class Main {
static ArrayList<Integer>[] shu; // 存储多叉树的子节点
static int[] f; // 存储每个节点的高度
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
shu = new ArrayList[n + 5]; // 初始化多叉树
Arrays.setAll(shu, i -> new ArrayList<>());
f = new int[n + 5]; // 初始化高度数组
// 构建多叉树
for (int i = 2; i <= n; i++) {
int parent = scan.nextInt(); // 读取父节点
shu[parent].add(i); // 将当前节点添加到父节点的子节点列表中
}
dfs(1); // 从根节点开始计算高度
// 找到最大高度
int max = 0;
for (int i = 1; i <= n; i++) {
max = Math.max(f[i], max);
}
System.out.println(max);
scan.close();
}
// DFS计算每个节点的高度
public static void dfs(int u) {
for (int child : shu[u]) {
f[child] = f[u] + shu[u].size(); // 当前节点的高度等于父节点的高度加 1
dfs(child); // 递归处理子节点
}
}
}
异或数列
题目描述
Alice 和 Bob 正在玩一个异或数列的游戏。初始时,Alice 和 Bob 分别有一个整数 aa 和 bb,初始值均为 00。
有一个给定的长度为 nn 的公共数列 X1,X2,⋯,XnX1,X2,⋯,Xn。Alice 和 Bob 轮流操作,Alice 先手,每步可以在以下两种选项中选一种:
选项 1:从数列中选一个 XiXi 给 Alice 的数异或上,或者说令 aa 变为 a⊕Xia⊕Xi。(其中 ⊕⊕ 表示按位异或)
选项 2:从数列中选一个 XiXi 给 Bob 的数异或上,或者说令 bb 变为 b⊕Xib⊕Xi。
每个数 XiXi 都只能用一次,当所有 XiXi 均被使用后(nn 轮后)游戏结束。游戏结束时,拥有的数比较大的一方获胜,如果双方数值相同,即为平手。 现在双方都足够聪明,都采用最优策略,请问谁能获胜?
输入描述
每个评测用例包含多组询问。询问之间彼此独立。
输入的第一行包含一个整数 T,表示询问数。
接下来 TT 行每行包含一组询问。其中第 ii 行的第一个整数 nini 表示数列长度,随后 nini 个整数 X1,X2,⋯,XniX1,X2,⋯,Xni 表示数列中的每个数。
输出描述
输出 TT 行,依次对应每组询问的答案。 每行包含一个整数 11、00 或−1−1 分别表示 Alice 胜、平局或败。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int T = sc.nextInt();
while (T-- > 0) {
solve(sc);
}
sc.close();
}
public static void solve(Scanner sc) {
int n = sc.nextInt();
int[] cnt = new int[25]; // 统计每一位的1的个数
int maxp = 0; // 记录最高位
for (int i = 0; i < n; i++) {
int t = sc.nextInt();
int p = 0;
while (t != 0) {
cnt[p] += (t & 1); // 统计当前位的1的个数
maxp = Math.max(p + 1, maxp); // 更新最高位
t >>= 1; // 右移一位
p++;
}
}
int result = 0;
for (int i = maxp - 1; i >= 0; i--) {
if ((cnt[i] & 1) == 1) { // 如果当前位的1的个数是奇数
if ((n & 1) == 1 || cnt[i] == 1) {
result = 1; // Alice胜
} else {
result = -1; // Bob胜
}
break;
}
}
System.out.println(result);
}
}
直线
题目描述
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。
在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上, 那么这些点中任意两点确定的直线是同一条。
给定平面上 2 × 3 个整点(x,y)∣0≤x<2,0≤y<3,x∈Z,y∈Z(x,y)∣0≤x<2,0≤y<3,x∈Z,y∈Z,即横坐标 是 00 到 11 (包含 0 和 1) 之间的整数、纵坐标是 0 到 2 (包含 0 和 2) 之间的整数 的点。这些点一共确定了 11 条不同的直线。
给定平面上 20×2120×21 个整点 (x,y)∣0≤x<20,0≤y<21,x∈Z,y∈Z(x,y)∣0≤x<20,0≤y<21,x∈Z,y∈Z,即横 坐标是 00 到 1919 (包含 00 和 1919) 之间的整数、纵坐标是 00 到 2020 (包含 00 和 2020) 之 间的整数的点。
请问这些点一共确定了多少条不同的直线。
import java.util.HashSet;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Set<String> lines = new HashSet<>();
// 遍历所有点对 (x1,y1) 和 (x2,y2)
for (int x1 = 0; x1 < 20; x1++) {
for (int y1 = 0; y1 < 21; y1++) {
for (int x2 = 0; x2 < 20; x2++) {
for (int y2 = 0; y2 < 21; y2++) {
if (x1 == x2 && y1 == y2) continue; // 跳过相同点
if (x1 == x2 || y1 == y2) continue; // 水平和垂直线后续处理
// 计算斜率 k
int deltaY = y2 - y1;
int deltaX = x2 - x1;
int gcd_k = gcd(Math.abs(deltaY), Math.abs(deltaX));
String k = (deltaY/gcd_k) + "/" + (deltaX/gcd_k);
// 规范分数符号(确保分母为正)
if (deltaX < 0) {
k = (-deltaY/gcd_k) + "/" + (-deltaX/gcd_k);
}
// 计算截距 b = (y1x2 - y2x1)/(x2 -x1)
int numerator = y1 * x2 - y2 * x1;
int denominator = deltaX;
int gcd_b = gcd(Math.abs(numerator), Math.abs(denominator));
String b = numerator/gcd_b + "/" + denominator/gcd_b;
// 规范截距的分数符号
if (denominator < 0) {
b = (-numerator/gcd_b) + "/" + (-denominator/gcd_b);
}
if (numerator == 0) {
b = "0/1"; // 零截距统一处理
}
lines.add("y = " + k + "x + " + b);
}
}
}
}
// 添加水平线 (y = c) 和垂直线 (x = c)
int verticalLines = 20; // x=0 到 x=19
int horizontalLines = 21; // y=0 到 y=20
System.out.println(lines.size() + verticalLines + horizontalLines);
}
// 计算最大公约数(处理负数)
public static int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
}
相乘
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 小蓝发现,他将 11 至 10000000071000000007 之间的不同的数与 20212021 相乘后再求除以 10000000071000000007 的余数,会得到不同的数。 小蓝想知道,能不能在 11 至 10000000071000000007 之间找到一个数,与 20212021 相乘后 再除以 10000000071000000007 后的余数为 999999999999999999。如果存在,请在答案中提交这个数; 如果不存在,请在答案中提交 00。
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
// for(long i = 1; i <=1000000007; i++){
// if(i*2021%1000000007== 999999999)
// System.out.println(i);
// }
System.out.println("17812964");
scan.close();
}
}