質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

1回答

434閲覧

同じアルゴリズムなのに計算量が違う理由を知りたい。

mldo0

総合スコア0

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2024/10/28 14:07

実現したいこと

計算量が異なる理由を知りたい。

前提

AtCoderのABC376、C問題を解いていました。リンク内容
自分の作成したコードと、回答は実装しようとしていることは同じはずなのですが、
計算量に差があります。

発生している問題・エラーメッセージ

以下の二つのコードの計算量が異なる理由を知りたいです。

該当のソースコード

c++

1#include <bits/stdc++.h> 2using namespace std; 3#define rep(i, n) for (int i = 0; i < n; ++i) 4#define ll long long 5 6int main(){ 7 int N; 8 9 cin >> N; 10 11 12 vector<ll> A(N); 13 vector<ll> B(N - 1); 14 15 rep(i, N){ 16 cin >> A[i]; 17 18 } 19 20 rep(i, N - 1){ 21 cin >> B[i]; 22 23 } 24 25 sort(A.begin(), A.end()); 26 27 vector<ll> D = B; 28 D.push_back(1 << 30); 29 sort(D.begin(), D.end()); 30 rep(i, N){ 31 if(D[i] < A[i]){ 32 cout << -1 << endl; 33 return 0; 34 } 35 } 36 ll right = A[N-1]; 37 ll left = A[0]; 38 ll val = (right + left) / 2; 39 ll ans = 0; 40 bool ok = false; 41 while(true){ 42 vector<ll> C = B; 43 C.push_back(val); 44 sort(C.begin(), C.end()); 45 ok = true; 46 47 rep(i,N){ 48 if(C[i] < A[i]){ 49 ok = false; 50 } 51 52 } 53 if(ok){ 54 ans = val; 55 right = val; 56 val = (right + left) / 2; 57 } 58 else{ 59 left = val; 60 ll reVal = val; 61 val = (right + left) / 2; 62 if(val == reVal){ 63 if(ans > 0){ 64 cout << ans << endl; 65 return 0; 66 } 67 } 68 } 69 70 } 71 72 return 0; 73} 74

c++

1#include <bits/stdc++.h> 2using namespace std; 3#define rep(i, n) for (int i = 0; i < n; ++i) 4#define ll long long 5 6int main() { 7 int N; 8 cin >> N; 9 10 vector<ll> A(N); 11 vector<ll> B(N - 1); 12 rep(i, N) cin >> A[i]; 13 rep(i, N - 1) cin >> B[i]; 14 15 sort(A.begin(), A.end()); // Aのソート 16 17 auto canSatisfy = [&](ll x) -> bool { 18 vector<ll> C = B; 19 C.push_back(x); 20 sort(C.begin(), C.end()); 21 22 rep(i, N) { 23 if (C[i] < A[i]) return false; // 条件を満たさない場合 24 } 25 return true; // 条件を満たす場合 26 }; 27 28 ll ok = 1 << 30; // 十分大きい値 29 ll ng = 0; // 最小値 30 31 // ok と ng の二分探索 32 if (!canSatisfy(ok)) { 33 cout << -1 << endl; // 最大値でも条件を満たさない場合 34 return 0; 35 } 36 37 while (ok - ng > 1) { 38 ll mid = (ok + ng) / 2; 39 if (canSatisfy(mid)) ok = mid; 40 else ng = mid; 41 } 42 43 cout << ok << endl; 44 return 0; 45} 46

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

toge_

2024/10/28 17:51

2つ質問させてください。 1. 「計算量」というのは「実行時間」のことだと考えればいいでしょうか?いわゆる「ランダウの記号」の意味だと同じ計算量になりそうに見えます。 2. 少なくとも上のコードと下のコードで以下の部分の実行時間が違うことは把握されているでしょうか? [上のコードの47-52行目] rep(i,N){ if(C[i] < A[i]){ ok = false; } } [下のコードの22-24行目] rep(i, N) { if (C[i] < A[i]) return false; // 条件を満たさない場合 }
mldo0

2024/10/29 05:53

ご質問ありがとうございます。 1.実行時間のことです。 2.上と下の実行時間の違いを理解していません。なぜ違いが生じるのでしょうか。 また、AtCoderで問題を解くにあたり、意識しなければならないほど大きな違いが生まれるのでしょうか。
toge_

2024/10/29 09:35

上のコードだと、強制的にN回分のデータの比較が発生します。 下のコードだと、条件に合致しない場合には即座に処理を終了します。 このため、下のコードの方が確実に高速に動作するロジックになります。 while(true)ループの中で毎回実行される内容のため、支配的ではありませんが、ある程度処理時間に影響を与える可能性があります。
mldo0

2024/10/29 13:23

break文が入っているのと同等ということですね。 言われるまで気づきませんでした。 ご回答ありがとうございました。
guest

回答1

0

上のコードに以下の入力を与えると、okfalseになることがないため、無限ループに陥ります。

2 1 2 2

二分探索は意外とバグりやすいので、「めぐる式二分探索」を使うことをお勧めします(下のコードでも使われています)。「二分探索アルゴリズムを一般化 〜 めぐる式二分探索法のススメ 〜」の解説が分かりやすいと思います。

追記

上のコードには、問題点が2つあります。上記入力例だと、そのうちの1つしか分からないので、もう1つの問題が分かる入力例も挙げておきます。こちらも無限ループに陥ります。

2 1 2 1

一応、問題点についても説明しておきます。自力で解きたいなら読み飛ばしてください。

  1. 条件を満たす場合に停止判定をしていない
    最初に挙げた入力例が無限ループとなる原因。
    条件を満たす場合の停止判定を追加するか、条件を満たさないことが確実な値(A[0]-1など)をleftの初期値に入れれば解決。
  2. 条件を満たすのが、最大のおもちゃと同じ大きさの箱だけだった場合、ansが0のまま更新されない
    今回挙げた入力例が無限ループとなる原因。
    そもそも27~35行の判定を通っている時点で、最大のおもちゃと同じ大きさの箱が条件を満たすのは明らかなので、ansの初期値をA[N-1]にすれば解決。(そうすると、ansrightが常に等しくなるので、ans自体不要となる)

投稿2024/10/28 14:55

編集2024/11/03 13:17
actorbug

総合スコア2381

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mldo0

2024/10/29 06:10

無限ループが起こってしまっているのは盲点でした。while(true)には注意が必要ですね... ありがとうございました。めぐる式を勉強してみます
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問