yas8
@yas8

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

atcoder ABC234 D javaで挑戦中 処理時間に悩まされてます...泣

こんにちは、java初めて2ヶ月!atcoderに若干ハマっているヤスと申します。
質問なのですが....

処理時間短縮する方法ありませんか??

この問題にjavaで挑戦中です....

あともう少しなのに...

スクリーンショット 2022-01-21 12.58.07.png

最初に書いたコードから調べに調べて処理時間を短縮してこの結果です...

あと、どこを短縮できるのかがわかりません..泣
(ちなみに解説をみても近しいところまではできるのですがjavaでどう書けばよいのか...)

今のソースコードこちら

java
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;

public class Main {


    public static void main(String[] args) {



        Scanner sc = new Scanner(System.in);

        String[] NK = sc.nextLine().split(" ");
        int N = Integer.parseInt(NK[0]);
        int K = Integer.parseInt(NK[1]);

        int numlist[] = Arrays.stream(sc.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        sc.close();

        Queue<Integer> iQlist = new PriorityQueue<Integer>();
        for(int i = 0; i < K; i++) {
        iQlist.add(numlist[i]);
        }
        Integer minnum = (Integer) iQlist.peek();
        System.out.println(minnum);

        for(int j = K; j < N; j++) {

            if(minnum < numlist[j]) {
                iQlist.add(numlist[j]);
                iQlist.remove(iQlist.peek());
                minnum = (Integer) iQlist.peek();
            }
            System.out.println(minnum);
        }

    }

}

自分で試したこと

・for文を減らす
・リスト減らす
・PriorityQueue使う

・splitが遅い? ← 調査中

0

4Answer

ぱっと思いつくのはPriorityQueueの初期容量(initialCapacity)を増やしておくことです。

(思い付きで言っているので全然違ったらごめんなさい)

参考:

0Like

splitでなくて、

    int[] numlist = new int[N];
    
    for (int i=0; i<N; i++) {
      numlist[i] = sc.nextInt();
    }
    

Scanner.nextInt()を使うとよいかと思います。

0Like

Comments

  1. @yas8

    Questioner

    ありがとうございます!!試してみます!
  2. @yas8

    Questioner

    クリアまではまだかかりますが、だいぶ早くなりました!!
    わざわざStringにして...分割して...ってやらなくてもよかったんですね〜
    ありがとうございました!!

Javaわからないマンです.適当なこと言います.

にあるように,iQlist.remove(iQlist.peek())は線形時間${\rm O}(N)$です(Chromeで上記リンクに飛んでください.この記述がある場所がハイライトされています.).

PriorityQueueにおいて計算量が${\rm O}({\rm log}N)$になってくれる動作は,Queueの先頭の値の削除poll,及び任意の値の挿入addです.Queueの先頭の値の読み込みpeekは${\rm O}(1)$です.

おそらく,これらに対してremove(Object)はPriorityQueueの得意とする先頭の値の削除ではなく任意の値を探索して削除するため,計算量は${\rm O}(N)$になりそうです.

したがって,iQlist.remove(iQlist.peek())のような書き方では,

  • iQlist.peek()を用いてQueueの先頭の値を取得.${\rm O}(1)$
  • iQlist.remove()で先ほど取得された値を指定しての削除.${\rm O}(N)$

になっていると思います.

改善案

先ほどのようなremove(Object)を用いた記述ではなくて,poll()を用いてQueueの先頭の値の削除を行うようにしましょう.poll()は,Queueの先頭の値の削除を${\rm O}({\rm log}N)$で行ってくれます.(ついでにその削除した値を返してくれます.)
したがって,以下のように修正すると良いと思います.

- iQlist.remove(iQlist.peek());
+ iQlist.poll();

改善前も改善後もどちらもQueueの先頭の値の削除という同じ動作ですが,もしかしたら使う関数によって計算量が違うかもしれないと思っての提案になります.

0Like

Comments

  1. 改善しなかったらごめんなさい!!!!
    改善したら,扱いたいデータ構造の各関数(今回で言えばPriorityQueueのadd, pool, peek, remove等)の計算量についても目を向ける機会にしていただければと思います!!!
  2. @yas8

    Questioner

    poll見落としてました...やってみます!
  3. @yas8

    Questioner

    pollでだいぶ早くなりました!!助かりました!ありがとうございます!
    完全クリアまであと210ms...あと少しなので頑張ります!
  4. あと何かあるとすれば,Integer型が遅そうですね,標準的なint型でやってみた方がよさげです。
  5. @yas8

    Questioner

    解決しました!!ありがとうございました!!
int[] numlist = new int[N-K];
Queue<Integer> iQlist = new PriorityQueue<Integer>((int)(K * 1.25));


for (int i=0; i<K; i++) {
  iQlist.add(sc.nextInt());
}

for (int i=0; i < N-K; i++) {
  numlist[i] = sc.nextInt();
}

// 中略

for(int j = 0; j < N-K; j++) {

  // 中略
}

とするともう少し速くなりそう。

0Like

Comments

  1. @yas8

    Questioner

    クリアできました!!ありがとうございました!

Your answer might help someone💌