我需要优化以下代码以减少内存。 我无法找到内存被浪费的地方。它给出了正确的结果。 可在此处查看问题陈述http://www.codechef.com/problems/FLIPCOIN/
import java.util.Scanner;
public class FLIPCOIN4 {
public static void main(String args[])
{
Scanner in = new Scanner(System.in);
//take N = no of coins, Q = no of commands as input
String s = in.nextLine();
String[] str = s.split(" ");
int N = Integer.parseInt(str[0]);
int Q= Integer.parseInt(str[1]);
int[] output = new int[Q];
int[] input;
//input - array of integers (one integer for 32 coins)
if(N%32>0)
input = new int[N/32 + 1];
else
input = new int[N/32];
//initialize to 0 = tails
for(int i=0;i<input.length;i++)
{
input[i] = 0;
}
int out = 0;
int temp1 = 0;
int temp2 = 0;
int command = 0;
int RangeL = 0;
int RangeR = 0;
//looping over all Q commands
while(Q>0) {
s = in.nextLine();
str = s.split(" ");
//command - command code
//RangeL,RangeR - range of coins which are affected
command = Integer.parseInt(str[0]);
RangeL = Integer.parseInt(str[1]);
RangeR = Integer.parseInt(str[2]);
// command==0 => if coins are to be flipped
if(command==0) {
//if the coins range is over multiple numbers in array input[]
if(RangeR/32>RangeL/32) {
if(RangeL%32==0)
input[RangeL/32] = input[RangeL/32] ^ -1;
else if(RangeL%32==1)
input[RangeL/32] =
input[RangeL/32] ^ Integer.MAX_VALUE;
else
input[RangeL/32] =
input[RangeL/32] ^ (int)Math.pow(2, 32-(RangeL%32));
if(RangeR%32==0)
input[RangeR/32] = input[RangeL/8] ^ Integer.MIN_VALUE;
else
input[RangeR/32] =
input[RangeR/8] ^
(Integer.MIN_VALUE
+ ( Integer.MAX_VALUE +1-
(int) Math.pow(2, 31-(RangeL%32))));
if(RangeR/32 - RangeL/32 > 1) {
for(int i=RangeL/32+1; i <RangeR/32 ; i++) {
input[i] = input[i] ^ -1;
}
}
}//if the coins range is contained in one single integer in input[]
else if(RangeR/32==RangeL/32) {
if(RangeL%32==0 && RangeR%32==0)
input[RangeL/32] = input[RangeL/32] ^
Integer.MIN_VALUE;
else if(RangeL%32==0 && RangeR%32==1)
input[RangeL/32] = input[RangeL/32] ^
(Integer.MIN_VALUE + (int) Math.pow(2, 30));
else if(RangeL%32==0 && RangeR%32 >1)
input[RangeL/32] = input[RangeL/32] ^
(Integer.MIN_VALUE + Integer.MAX_VALUE +1 -
(int) Math.pow(2, 31-(RangeR%32)));
else if(RangeL%32==1 && RangeR%32 ==1)
input[RangeL/32] = input[RangeL/32] ^
(int) Math.pow(2, 30);
else if(RangeL%32==1 && RangeR%32 >1)
input[RangeL/32] = input[RangeL/32] ^
(Integer.MAX_VALUE +1 -
(int) Math.pow(2, 31-(RangeR%32)));
else
input[RangeL/32] = input[RangeL/32] ^
((int) Math.pow(2, 32-(RangeL%32)) -
(int) Math.pow(2, 31-(RangeR%32)) );
}
}// command==1 => no of heads is to be reported
else if(command==1) {//if the coins range is contained in a single integer
if(RangeR/32 == RangeL/32) {
temp1 = input[RangeL/32]<< RangeL%32;
temp1 = temp1 >>> RangeL%32;
temp1 = temp1 >>> (31 - RangeR%32);
temp1 = temp1 << (31 - RangeR%32);
output[out] = Integer.bitCount(temp1);
}
//if the coins range is over multiple numbers in array input[]
else if(RangeR/32>RangeL/32) {
temp1 = input[RangeL/32]<< RangeL%32;
temp1 = temp1 >>> RangeL%32;
temp2 = input[RangeL/32] >>> (31 - RangeR%32);
temp2 = temp2 << (31 - RangeR%32);
output[out] =
Integer.bitCount(temp1)+ Integer.bitCount(temp2);
}
if(RangeR/32 - RangeL/32 > 1) {
for(int i=RangeL/32+1; i <RangeR/32 ; i++) {
output[out] = output[out] + Integer.bitCount(input[i]);
}
}
out++;
}
Q--;
}
for(int i =0;i<out;i++) {
System.out.println(output[i]);
}
}
}
答案 0 :(得分:2)
关于你的程序的一些一般性评论(与内存问题无关,但可能有助于解决它):
import java.util.Scanner;
public class FLIPCOIN4 {
你的班级名字有点奇怪 - 这是以某种方式强制执行的任务吗?
(在Java中,对于类名,通常使用camel-case。FlipCoin
将是一个示例。)
public static void main(String args[])
{
您正在使用这个非常大的方法进行所有处理。更好地构建代码,将其放在几个方法中,每个方法只有一个任务。
Scanner in = new Scanner(System.in);
//take N = no of coins, Q = no of commands as input
String s = in.nextLine();
String[] str = s.split(" ");
int N = Integer.parseInt(str[0]);
int Q= Integer.parseInt(str[1]);
Scanner
课程的方法多于nextLine()
- 如果你直接使用nextInt
,你的课程会缩短很多。
int[] output = new int[Q];
您需要output
数组用于什么?您可以在生成后立即输出每个数字。这样可以节省200 kB(如果他们的测试输入真的是Q = 50000)。
int[] input;
//input - array of integers (one integer for 32 coins)
if(N%32>0)
input = new int[N/32 + 1];
else
input = new int[N/32];
你可以在这里使用[(N-1)/32 +1]
并避免这两种情况,但我不确定这是否真的更具可读性。
input
数组名称错误:它不是输入,但它代表硬币的当前状态。
//initialize to 0 = tails
for(int i=0;i<input.length;i++)
{
input[i] = 0;
}
使用0
(或'\0'
或0.0
或null
)自动初始化数组元素,因此完全不需要此循环。
int out = 0;
int temp1 = 0;
int temp2 = 0;
int command = 0;
int RangeL = 0;
int RangeR = 0;
大多数这些变量在首次使用时会被更好地声明(和初始化)。
//looping over all Q commands
while(Q>0) {
s = in.nextLine();
str = s.split(" ");
//command - command code
//RangeL,RangeR - range of coins which are affected
command = Integer.parseInt(str[0]);
RangeL = Integer.parseInt(str[1]);
RangeR = Integer.parseInt(str[2]);
如前所述,您可以轻松地使用in.nextInt()
。
我还认为s.split
方法每次创建一个新的正则表达式模式,这不会花费太长时间,但仍然是多余的。
// command==0 => if coins are to be flipped
对于命令,你真的不需要将它解析为int
,使用String比较也可以。
if(command==0) {
//if the coins range is over multiple numbers in array input[]
if(RangeR/32>RangeL/32) {
if(RangeL%32==0)
您反复使用四个数字RangeR/32
,RangeL/32
,RangeR%32
和RangeL%32
。将它们分配给适当的变量(甚至常量,final
) - 这首先会使你的程序更快,而且(更重要的是)如果你使用正确的名称,则更容易辨认。
input[RangeL/32] = input[RangeL/32] ^ -1;
else if(RangeL%32==1)
input[RangeL/32] =
input[RangeL/32] ^ Integer.MAX_VALUE;
else
input[RangeL/32] =
input[RangeL/32] ^ (int)Math.pow(2, 32-(RangeL%32));
不要将Math.pow用于小幂2的整数指数。
2 x 可写为1 << x
(如果0 <= x <32)。
if(RangeR%32==0)
input[RangeR/32] = input[RangeL/8] ^ Integer.MIN_VALUE;
else
input[RangeR/32] =
input[RangeR/8] ^
(Integer.MIN_VALUE
+ ( Integer.MAX_VALUE +1-
(int) Math.pow(2, 31-(RangeL%32))));
嗯,这是一个残酷的公式。你知道Integer.MAX_VALUE + 1 == Integer.MIN_VALUE吗?
if(RangeR/32 - RangeL/32 > 1) {
for(int i=RangeL/32+1; i <RangeR/32 ; i++) {
input[i] = input[i] ^ -1;
}
}
你的循环在没有if-check之前会有相同的效果(那时它将是一个空循环)。
}//if the coins range is contained in one single integer in input[]
else if(RangeR/32==RangeL/32) {
此评论看起来与闭幕大括号有关,而实际上与以下if
有关。
if(RangeL%32==0 && RangeR%32==0)
input[RangeL/32] = input[RangeL/32] ^
Integer.MIN_VALUE;
else if(RangeL%32==0 && RangeR%32==1)
input[RangeL/32] = input[RangeL/32] ^
(Integer.MIN_VALUE + (int) Math.pow(2, 30));
else if(RangeL%32==0 && RangeR%32 >1)
input[RangeL/32] = input[RangeL/32] ^
(Integer.MIN_VALUE + Integer.MAX_VALUE +1 -
(int) Math.pow(2, 31-(RangeR%32)));
else if(RangeL%32==1 && RangeR%32 ==1)
input[RangeL/32] = input[RangeL/32] ^
(int) Math.pow(2, 30);
else if(RangeL%32==1 && RangeR%32 >1)
input[RangeL/32] = input[RangeL/32] ^
(Integer.MAX_VALUE +1 -
(int) Math.pow(2, 31-(RangeR%32)));
else
input[RangeL/32] = input[RangeL/32] ^
((int) Math.pow(2, 32-(RangeL%32)) -
(int) Math.pow(2, 31-(RangeR%32)) );
如前所述:将其放在另一种方法中,不要使用Math.pow。
}
}// command==1 => no of heads is to be reported
else if(command==1) {//if the coins range is contained in a single integer
if(RangeR/32 == RangeL/32) {
temp1 = input[RangeL/32]<< RangeL%32;
temp1 = temp1 >>> RangeL%32;
temp1 = temp1 >>> (31 - RangeR%32);
temp1 = temp1 << (31 - RangeR%32);
啊,你知道如何换班: - )
也可以掩盖它们,而不是将这些位移掉。 final int mask = 1<<(32-RangeL%32) - 1<<(31-RangeR%32)
或类似内容,然后是temp1 = mask & input[RangeL/32]
。
output[out] = Integer.bitCount(temp1);
}
//if the coins range is over multiple numbers in array input[]
else if(RangeR/32>RangeL/32) {
temp1 = input[RangeL/32]<< RangeL%32;
temp1 = temp1 >>> RangeL%32;
temp2 = input[RangeL/32] >>> (31 - RangeR%32);
temp2 = temp2 << (31 - RangeR%32);
output[out] =
Integer.bitCount(temp1)+ Integer.bitCount(temp2);
}
if(RangeR/32 - RangeL/32 > 1) {
for(int i=RangeL/32+1; i <RangeR/32 ; i++) {
output[out] = output[out] + Integer.bitCount(input[i]);
}
}
我之前说过的内容也适用于此循环。
out++;
此时你可以输出结果,而根本没有这个数组。
}
Q--;
}
for(int i =0;i<out;i++) {
System.out.println(output[i]);
}
(此时不需要输出。)
}
}
要考虑的另一件事:如果你反过来命令这些位,你就不必在所有的指数中使用这些减法。
使用类似java.util.BitSet的东西会使你的整个程序变得非常短,因为这会为你完成所有的逻辑逻辑。
答案 1 :(得分:1)
所以,让我们看看你的程序,特别是你的内存分配。
int[] output = new int[Q];
int[] input;
//input - array of integers (one integer for 32 coins)
if(N%32>0)
input = new int[N/32 + 1];
else
input = new int[N/32];
这里你要创建两个大数组。如果N和Q都最大为50000,那么这些只需要206250个字节,加上一点开销。
s = in.nextLine();
str = s.split(" ");
这是在循环中完成的,每个都创建一个新字符串(从输入读取)和一个新字符串[]。也许还有一个新的Regexp模式和匹配器(我不知道这些是否被追逐)。但无论如何,你只保留这些对象直到下一次迭代,所以垃圾收集器应该能够扔掉它们。
总而言之,没有任何理由让您的程序在运行50000(短)行长输入时需要177 MB。
我认为CodeChef的评估程序有点愚蠢,你不应该担心它说的是什么。也许它给程序一个非常大的堆,所以他们不需要做任何垃圾收集,然后当它们达到177 MB时杀死它们。 (它也因为跑得太久而拒绝了我的答案。)
我会在另一个答案中向您提供有关您的计划的一般提示。