【发布时间】:2014-04-29 23:57:13
【问题描述】:
我正在使用线段树解决this problem。我在每个节点保存总和、最大值、最左边的最大值和最右边的最大值。然后我搜索图表以找到特定区间的答案。我怎样才能提高这段代码的速度?
import java.util.Scanner;
//TLE
class GSS1 {
static class Node{
int max;
int MaxL;
int MaxR;
int sum;
public Node(int max, int MaxL, int MaxR, int sum){
this.max=max;
this.MaxL=MaxL;
this.MaxR=MaxR;
this.sum=sum;
}
public Node(){
}
}
static class SegmentTree{
private Node[] tree;
private int maxsize;
private int height;
private final int STARTINDEX = 0;
private final int ENDINDEX;
private final int ROOT = 0;
Node s;
public SegmentTree(int size){
height = (int)(Math.ceil(Math.log(size) / Math.log(2)));
maxsize = 2 * (int) Math.pow(2, height) - 1;
tree = new Node[maxsize];
for(int i=0;i<tree.length;i++){
tree[i]=new Node();
}
ENDINDEX = size - 1;
s=new Node();
s.MaxL=Integer.MIN_VALUE;
s.MaxR=Integer.MIN_VALUE;
s.sum=Integer.MIN_VALUE;
s.max=Integer.MIN_VALUE;
}
private int leftchild(int pos){
return 2 * pos + 1;
}
private int rightchild(int pos){
return 2 * pos + 2;
}
private int mid(int start, int end){
return (start + (end - start) / 2);
}
private Node constructSegmentTreeUtil(int[] elements, int startIndex, int endIndex, int current){
if (startIndex == endIndex)
{
tree[current].max=tree[current].MaxL=tree[current].MaxR=tree[current].sum=elements[startIndex];
return tree[current];
}
int mid = mid(startIndex, endIndex);
Node left=constructSegmentTreeUtil(elements, startIndex, mid, leftchild(current));
Node right=constructSegmentTreeUtil(elements, mid + 1, endIndex, rightchild(current));
tree[current].max = Math.max(left.max, right.max);
tree[current].MaxL = Math.max(left.MaxL , left.sum+right.MaxL);
tree[current].MaxR = Math.max(right.MaxR , right.sum+left.MaxR);
tree[current].sum = left.sum+right.sum;
return tree[current];
}
public void constructSegmentTree(int[] elements){
constructSegmentTreeUtil(elements, STARTINDEX, ENDINDEX, ROOT);
}
private Node getSumUtil(int startIndex, int endIndex, int queryStart, int queryEnd, int current){
if (queryStart <= startIndex && queryEnd >= endIndex ){
return tree[current];
}
if (endIndex < queryStart || startIndex > queryEnd){
return s;
}
int mid = mid(startIndex, endIndex);
Node left=getSumUtil(startIndex, mid, queryStart, queryEnd, leftchild(current));
Node right=getSumUtil( mid + 1, endIndex, queryStart, queryEnd, rightchild(current));
Node current_Node=new Node();
current_Node.max = Math.max(left.max, right.max);
current_Node.MaxL = Math.max(left.MaxL , left.sum+right.MaxL);
current_Node.MaxR = Math.max(right.MaxR , right.sum+left.MaxR);
current_Node.sum = left.sum+right.sum;
return current_Node;
}
public int getMaxSum(int queryStart, int queryEnd){
if(queryStart < 0 || queryEnd > tree.length)
{System.out.println("inside negative");
return Integer.MIN_VALUE;
}
return getMax(getSumUtil(STARTINDEX, ENDINDEX, queryStart, queryEnd, ROOT));
}
public int getMax(Node r){
return Math.max(Math.max(r.max, r.MaxL),Math.max(r.MaxR, r.sum));
}
public int getFirst(){
return tree[0].MaxL;
}
}
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int numbers[]=new int [input.nextInt()];
for(int i=0;i<numbers.length;i++){
numbers[i]=input.nextInt();
}
SegmentTree tree=new SegmentTree(numbers.length);
tree.constructSegmentTree(numbers);
int cases=input.nextInt();
int x;
int y;
int query;
for(int i=0;i<cases;i++){
x=input.nextInt()-1;
y=input.nextInt()-1;
System.out.println(tree.getMaxSum(x, y));
}
}
}
【问题讨论】:
-
你的算法的复杂度是多少?您是否尝试过在随机输入上运行它并检查需要多长时间?根据我的经验,您应该至少获得现代机器时间限制的四分之一的运行时间,以便它通过 Pyramid
-
它通过前 8 个测试用例,但在第九个测试用例失败,所以我假设第 9 个测试用例的输入最大。我认为我的算法的复杂度是 O(nlogn)
-
@TheBear:我认为只有少数人能够使用 Java 解决这个问题并非巧合。时间限制非常严格,因此如果您想坚持使用 Java,您可能需要进行大量的持续优化才能通过。也许使用数组来表示树(如隐式堆)比一次分配每个节点要快。 I/O 速度可能也是这里的一个问题,
Scanner非常慢,这就是为什么人们通常使用他们自己的缓冲 I/O 整数解析例程来进行竞争性编程。 -
@PhamTrung 是的,你是对的,应该是
tree[current].max = Math.max(left.max, Math.max(right.max, left.maxR + right.maxL)); -
我无法想象你不会通过随机测试发现这一点。 总是当您不知道您的代码在哪些情况下不起作用时,请在 SPOJ 或类似网站上使用随机测试
标签: java algorithm optimization segment-tree