51,411
社区成员
发帖
与我相关
我的任务
分享今天在做算法题时遇到了内存超限,尽力优化也有一个超限。找了一些其他人的解法,对比发现关键只在两行的区别。
题目:
有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。 你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。 请问如何走才能拿到最多的金币。输入n,n*n的矩阵,金币数不超过1000(正整数) 输出最多拿多少金币
import java.util.Scanner;
public class njb {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] coins = new int[n][n];
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
coins[i][j] = sc.nextInt();
}
}
//这里设置M是为了防止内存超限,不是冗余
int M = money(coins);
System.out.println(M);
//System.out.println(money(coins));
}
static int money(int[][] n){
int [][] dp = new int[n.length][n.length];
dp[0][0] = n[0][0];
for (int i = 0; i < n.length; i++) {
for (int j = 0; j < n.length; j++) {
if (i == 0 && j>0) {
dp[0][j] = dp[0][j - 1] + n[0][j];
} else if (j == 0 && i>0) {
dp[i][0] = dp[i - 1][0] + n[i][0];
} else if(i > 0) {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + n[i][j];
}
}
}
return dp[n.length - 1][n.length - 1];
}
}
关键在于M,不知道原理为何,求好心大佬解答 orz
在你描述的问题中,内存超限的问题主要出现在递归或者大量数据的情况下,Java会遇到内存限制。虽然你的代码实现中使用了动态规划来解决问题,但仍然可能由于N值较大而导致内存使用过多。
你的代码在解决最大金币问题时,使用了一个二维数组 dp 来存储每个位置上最大金币数的计算结果,这样的方式是常见的动态规划方法。关键是你提到的 M 变量,实际上在你的代码中 M 变量并不真正影响内存使用情况,可能是你在调试或测试过程中发现的一个误解。
内存超限问题的来源
内存消耗大:当N值很大时,创建一个N x N的二维数组会消耗大量内存。每个整型变量占用4字节,因此如果N过大,整个二维数组可能占用超过Java虚拟机的堆内存限制。
递归深度:如果使用递归算法且递归深度过深(例如没有优化的深度优先搜索),会导致栈内存溢出。
解决方法
优化动态规划表的大小:如果内存超限,首先要考虑的是减少内存的使用。例如在动态规划中,可以通过优化存储结构来减少内存使用。例如在这个问题中,只需要使用一维数组来保存当前行和上一行的最大金币数。
增大Java虚拟机堆内存:可以通过调整Java虚拟机参数来增加堆内存,例如 -Xmx 参数。
避免不必要的内存分配:检查代码中是否有不必要的内存分配,比如冗余的对象创建。
下面是使用一维数组优化动态规划存储的方法:
import java.util.Scanner;
public class njb {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] coins = new int[n][n];
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
coins[i][j] = sc.nextInt();
}
}
int M = money(coins);
System.out.println(M);
}
static int money(int[][] n){
int[] dp = new int[n.length];
dp[0] = n[0][0];
for (int i = 0; i < n.length; i++) {
for (int j = 0; j < n.length; j++) {
if (i == 0 && j > 0) {
dp[j] = dp[j - 1] + n[i][j];
} else if (j == 0 && i > 0) {
dp[j] += n[i][j];
} else if (i > 0 && j > 0) {
dp[j] = Math.max(dp[j], dp[j - 1]) + n[i][j];
}
}
}
return dp[n.length - 1];
}
}
在这个优化版本中,我们使用一个一维数组 dp 来存储每行的最大金币数,这样可以将内存使用从 O(N^2) 降低到 O(N)。
内存优化的原理
状态压缩:通过使用一维数组代替二维数组,我们在每次迭代中仅保存当前行和上一行的状态,从而减少内存使用。
增大堆内存:可以通过运行时参数增加堆内存,避免因内存不足导致程序崩溃。
希望这些解释和优化方法能帮助你理解和解决Java中内存超限的问题。
这个题目有问题吧, 因为题目限制只能向右边或下边走,所以, 移动的步数最大是 2n-1, 因此,
最多拿多少金币 = (2n-1) * 每个格子的金币数
方法一执行就会入栈,执行完毕就会出栈
相对比,多嵌了一层,开销相对就大了一点