数组元素之和最小化
考察: 数字逻辑思维
功能亮点 多维度分析, 引导刷题方向
刷题实践:
首先借助MarsCode AI 读懂题目
对题目的样例进行分析
得到分析结果:
题目分析
题目要求构造一个包含 ( n ) 个元素的数组,并且满足以下条件:
- 数组中的所有元素两两不同:这意味着数组中的每个元素都必须是唯一的。
- 数组所有元素的最大公约数为 ( k ) :这意味着数组中的每个元素都必须是 ( k ) 的倍数。
- 数组元素之和尽可能小:我们需要找到一组满足上述条件的数组,使得它们的和最小。
样例解释
- 样例 1:
-
- 输入:( n = 3 ), ( k = 1 )
- 输出:6
- 解释:当 ( k = 1 ) 时,数组中的每个元素可以是任意正整数。
为了使数组元素之和最小,我们可以选择最小的 ( n ) 个正整数:1, 2, 3。
这些数的和为 ( 1 + 2 + 3 = 6 )。
- 样例 2:
-
- 输入:( n = 2 ), ( k = 2 )
- 输出:6
- 解释:当 ( k = 2 ) 时,数组中的每个元素必须是 2 的倍数。
为了使数组元素之和最小,我们可以选择最小的 ( n ) 个 2 的倍数:2, 4。
这些数的和为 ( 2 + 4 = 6 )。
- 样例 3:
-
- 输入:( n = 4 ), ( k = 3 )
- 输出:30
- 解释:当 ( k = 3 ) 时,数组中的每个元素必须是 3 的倍数。
为了使数组元素之和最小,我们可以选择最小的 ( n ) 个 3 的倍数:3, 6, 9, 12。
这些数的和为 ( 3 + 6 + 9 + 12 = 30 )。
解题思路
- 确定数组元素的范围:由于数组中的每个元素必须是 ( k ) 的倍数,因此我们可以从 ( k ) 开始,依次选择 ( k ) 的倍数。
- 选择最小的 ( n ) 个 ( k ) 的倍数:为了使数组元素之和最小,我们只需要选择最小的 ( n ) 个 ( k ) 的倍数。
- 计算这些数的和:将这些数相加,得到的结果就是数组元素之和的最小值。
伪代码
int minSum(int n, int k) {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i * k;
}
return sum;
}
根据AI 给出的伪代码已经很明显了,解题方法就是,累加n个(1-n)*k的和。
我的思考:
因为最小的数列是一个等差数列所以我们可以利用公式求和 (a1+an)*n/2
public static int solution(int n, int k) {
int a = n*k;
int result = (k+a)*n/2;
return result;
}
AC代码
public class Main {
public static int solution(int n, int k) {
int a = n*k;
int result = (k+a)*n/2;
return result;
}
public static void main(String[] args) {
System.out.println(solution(3, 1) == 6);
System.out.println(solution(2, 2) == 6);
System.out.println(solution(4, 3) == 30);
}
}
小U的最大连续移动次数问题
考察: 搜索算法
我比较蠢一开始题目样例没有看懂。然后MarsCode AI 启动!!!
样例解释
样例 1
- 解释:
-
- 地图是一个 2x2 的矩阵,包含以下高度:
3 4
1 2
-
- 小 U 可以选择的移动顺序是:3 -> 4 -> 1 -> 2,这样他可以移动 3 次。
-
-
- 从 3 到 4 是上坡。
- 从 4 到 1 是下坡。
- 从 1 到 2 是上坡。
-
-
- 这个顺序满足题目中的所有条件:交替上坡和下坡,每个位置只经过一次。
样例 2
- 解释:
-
- 地图是一个 3x3 的矩阵,包含以下高度:
3 9 5
10 1 6
2 7 4
-
- 小 U 可以选择的移动顺序是:3 -> 9 -> 5 -> 10 -> 1 -> 6 -> 2 -> 7 -> 4,这样他可以移动 8 次。
-
-
- 从 3 到 9 是上坡。
- 从 9 到 5 是下坡。
- 从 5 到 10 是上坡。
- 从 10 到 1 是下坡。
- 从 1 到 6 是上坡。
- 从 6 到 2 是下坡。
- 从 2 到 7 是上坡。
- 从 7 到 4 是下坡。
-
-
- 这个顺序满足题目中的所有条件:交替上坡和下坡,每个位置只经过一次。
我这个小辣鸡看了样例还是没有啥思路,于是我又让ai给了点提示 (目前是学习阶段所以我问ai的比较多,后期有一定基础之后就不会这样啦)
看到AI给的提示 用BFS或者DFS!!!!我靠 这个题目不就是这样吗,给你一个网格图形然后问你什么最小!!! 这不是典型的搜索问题吗!!!AI都能秒杀想到,我怎么没有想到。我不会比AI还傻吧,嘿嘿嘿(有点倒反天罡)
问题分析:
小U在地图上行走,要求:
- 只能上坡或下坡,不能走到高度相同的点。
- 移动时必须交替进行:上坡后必须下坡,下坡后必须上坡,不能连续上坡或下坡。
- 每个位置只能经过一次,不能重复行走。
目标是找到小U在满足上述条件下,能够移动的最大次数,即最大的移动步数。
这里我选择DFS不要问为什么,因为这个比较熟练 。当然BFS应该也可以写,回头我试试哈哈哈
解决思路:
我们可以对地图中的每个点进行深度优先搜索(DFS),在搜索过程中:
- 记录当前位置
x, y。 - 记录上一次的移动类型
lastMove,1表示上坡,2表示下坡,0表示起点(无移动)。 - 使用一个
visited数组来记录已经访问过的点,避免重复。
在每次递归中,尝试移动到四个方向的相邻点,判断是否满足以下条件:
- 相邻点未被访问过。
- 相邻点的高度与当前点不同(不能走到高度相同的点)。
- 根据
lastMove判断移动方向是否符合交替上坡下坡的要求。
AC代码:
public class Main {
public static int solution(int m, int n, int[][] a) {
int maxMoves = 0;
boolean[][] visited = new boolean[m][n];
// 从每个点开始进行DFS
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
maxMoves = Math.max(maxMoves, dfs(a, visited, i, j, 0, m, n) - 1);
}
}
return maxMoves;
}
// lastMove: 0 = 起点,1 = 上一次是上坡,2 = 上一次是下坡
private static int dfs(int[][] a, boolean[][] visited, int x, int y, int lastMove, int m, int n) {
visited[x][y] = true;
int maxLength = 1; // 当前路径长度
int[] dx = {-1, 1, 0, 0}; // 上下
int[] dy = {0, 0, -1, 1}; // 左右
for (int dir = 0; dir < 4; dir++) {
int nx = x + dx[dir];
int ny = y + dy[dir];
// 判断新坐标是否在地图范围内且未被访问过
if (nx >= 0 && nx < m && ny >= 0 && ny < n && !visited[nx][ny]) {
int heightDiff = a[nx][ny] - a[x][y];
if (heightDiff != 0) { // 不能走到高度相同的点
int newLastMove = 0;
boolean canMove = false;
if (lastMove == 0) {
// 起点,可以上坡或下坡
canMove = true;
newLastMove = heightDiff > 0 ? 1 : 2;
} else if (lastMove == 1 && heightDiff < 0) {
// 上一次是上坡,下一次必须下坡
canMove = true;
newLastMove = 2;
} else if (lastMove == 2 && heightDiff > 0) {
// 上一次是下坡,下一次必须上坡
canMove = true;
newLastMove = 1;
}
if (canMove) {
int length = 1 + dfs(a, visited, nx, ny, newLastMove, m, n);
maxLength = Math.max(maxLength, length);
}
}
}
}
visited[x][y] = false; // 回溯
return maxLength;
}
public static void main(String[] args) {
System.out.println(solution(2, 2, new int[][]{{1, 2}, {4, 3}}) == 3);
System.out.println(solution(3, 3, new int[][]{{10, 1, 6}, {5, 9, 3}, {7, 2, 4}}) == 8);
System.out.println(solution(4, 4, new int[][]{{8, 3, 2, 1}, {4, 7, 6, 5}, {12, 11, 10, 9}, {16, 15, 14, 13}}) == 11);
}
}
备注:
- visited 数组: 用于记录当前路径中已经访问过的点,防止重复访问。
- 方向数组 dx 和 dy: 用于遍历当前点的四个相邻方向(上、下、左、右)。
- 移动规则: 根据
lastMove判断下一步是上坡还是下坡,确保上坡和下坡交替进行。 - 递归搜索: 通过 DFS 遍历所有可能的路径,更新
maxLength,并在回溯时重置visited数组对应的位置。
有个容易错误的地方:
maxLength表示当前路径的节点数,因此移动次数应为maxLength - 1。
我的总结:这个题目和普通的DFS有一点点,就是不能顺便乱走,只能一上一下,而且限制条件多,很适合练手gogogogo。写完这个题目我感觉对DFS影响更加深刻了,算法还是得多写才能进步快快
AI 刷题优势分析:
在这个解题过程中,AI 刷题平台的功能对我有以下帮助:
- 智能解题分析: 在我初步完成代码后,平台提供了详尽的解题报告,指出了代码中可能存在的边界问题,并提醒我注意的细节。这使我在提交之前就修正了潜在的错误。
- 即时反馈与交互式调试: 在云端编辑器中,我可以方便地运行测试用例,查看输出结果。当结果不符合预期时,平台还提供了错误定位和可能的改进建议。
学习收获与体会:
通过这次刷题实践,我深刻体会到了DFS强大之处。关键在于找到合适的状态表示和转移方程,简化问题的复杂度。
AI 刷题平台的个性题库让我能够有针对性地练习,提高了学习效率。而智能解题分析则帮助我加深了对算法的理解,避免了许多常见的陷阱。
最重要的肯定是这样啦!!!这种交互式的学习方式激发了我的学习兴趣,使得算法学习不再枯燥,而是充满了探索和发现的乐趣。
最后:
算法的学习不仅需要大量的练习,更需要高质量、有针对性的训练。AI 刷题平台通过个性化的题目推荐和智能的解题分析,帮助我在学习过程中少走弯路