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

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

新規登録して質問してみよう
ただいま回答率
85.48%
ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

C++

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

Q&A

解決済

2回答

1572閲覧

[C++][AtCoder/競プロ]二重ループ使用の際に、AtCoderにてTLE(実行時間制限超過)となるのを防ぎたい。

Kaguya_4869

総合スコア116

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

C++

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

0グッド

0クリップ

投稿2021/12/19 12:53

前提・実現したいこと

こんにちは。
現在、こちらの問題を解いています。
そこで、実行時間制限超過のエラーが出てしまっているので、直したいと考えています。

ソースコード

こちらの問題を解いていて、コードを以下のように書きました。

C++

1#include <bits/stdc++.h> 2 3using namespace std; 4 5int main() { 6 int N, Q; 7 cin >> N >> Q; 8 vector<int> a(N); 9 vector<int> x(Q); 10 11 int count=0; 12 13 for (int i=0; i<N; i++) { 14 cin >> a.at(i); 15 } 16 for (int i=0; i<Q; i++) { 17 cin >> x.at(i); 18 } 19 20 sort(a.begin(), a.end()); 21 for (int i=0; i<Q; i++) { 22 for (int j=0; j<N; j++) { 23 if (a.at(j) >= x.at(i)) { 24 count++; 25 } 26 if (j == N-1) { 27 cout << count << endl; 28 count = 0; 29 } 30 } 31 } 32}

発生している問題

上記のコードを提出すると、TLE(実行時間制限超過)と表示されてしまいます。
恐らく、二重ループを使用しているところで計算量が大きくなってしまっているため、エラーが発生しているのではないかと思っております。
しかしながら、二重ループ以外で、どう書き換えればいいのかが分かりません。

ざっくりとした質問になってしまいましたが、ご教授いただけると幸いです。

よろしくお願い致します。

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

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

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

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

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

Crimson_Tide

2021/12/19 15:23

二重ループで最大で10^9 × 10^9 してるので現実的ではないですね。 線形探索では厳しいと思います。 https://atcoder.jp/contests/abc231/editorial/3074 公式解説が既にあるのでご覧になるとよいかと思います。 ソートした配列に対して二分探索することでxより大きくなる要素番号を見つけ、Nからその要素番号を引いてますね。 https://atcoder.jp/contests/abc231/editorial/3075 さらにもう1つの解説動画でクエリ(xの配列)のソートをしてます。 具体的な処理は見ていませんが、恐らく二分探索の範囲を徐々に狭め探索回数の軽減につなげているではないかと思います。
Kaguya_4869

2021/12/19 23:17

Crimson_Tide様 公式解説の更に詳しい解説、ありがとうございます。 二分探索が少し苦手なため、今まで避けてきていたのですが、 この機会に正面から向き合ってみようと思います! ありがとうございます。
episteme

2021/12/20 00:27 編集

> https://atcoder.jp/contests/abc231/editorial/3075 > さらにもう1つの解説動画でクエリ(xの配列)のソートをしてます。 > 具体的な処理は見ていませんが、恐らく二分探索の範囲を徐々に狭め探索回数の軽減につなげているで > はないかと思います。 ホントにこっちの方が速いか疑わしい...二分検索してないっポいし。
guest

回答2

0

ベストアンサー

配列aをソートしたならば、全ての要素を探索せずとも、x以上である最小の要素a[i]を見つければ、身長がx以上の生徒の人数は(N-i)人と求められます。また、最小の要素が見つからなかった場合も、i=Nとすることで同様に求められます。

そして、最小の要素a[i]を求めるには、線形探索を使うと一回の質問ごとにO(N)かかってしまうので、二分探索を使います。すると質問ごとの計算量をO(logN)に抑えることができ、全体の計算量がO(NlogN)となって通ります。なお、二分探索を使うには配列aがソートされている必要があります。

二分探索にはlower_bound関数を使いましょう。ただし、lower_boundの返り値はイテレーターなので、返り値からa.begin()を引くことで、iを求めることができます。(今回はa.end()から返り値を引けば、生徒の人数を直接求められます。)

コード例:

C++

1#include <bits/stdc++.h> 2 3using namespace std; 4 5int main() 6{ 7 int N, Q; 8 cin >> N >> Q; 9 vector<int> a(N); 10 vector<int> x(Q); 11 12 for (int i = 0; i < N; i++) 13 { 14 cin >> a.at(i); 15 } 16 for (int i = 0; i < Q; i++) 17 { 18 cin >> x.at(i); 19 } 20 21 sort(a.begin(), a.end()); 22 for (int i = 0; i < Q; i++) 23 { 24 cout << a.end() - lower_bound(a.begin(), a.end(), x[i]) << endl; 25 //cout << N - (lower_bound(a.begin(), a.end(), x[i]) - a.begin()) << endl; 26 } 27} 28

二分探索について詳しく知りたい場合は、こちらのサイトが参考になると思います。
https://pyteyon.hatenablog.com/entry/2019/02/20/194140

投稿2021/12/19 15:03

編集2021/12/19 15:04
luuguas

総合スコア492

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

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

episteme

2021/12/19 21:40

> a.end() - lower_bound(a.begin(), a.end(), x[i]) あるいは distance(lower_bound(a.begin(), a.end(), x[i]), a.end())
Kaguya_4869

2021/12/19 23:35

luuguas様 回答ありがとうございます。 二分探索についての分かりやすいサイトまで紹介してくださり、 本当にありがとうございます。 理解することができました! これから、もう少し二分探索についての問題等を解いていきたいと思います。
luuguas

2021/12/20 13:19

二分探索は競プロの典型テクニックなので、使いこなせるようにしておきましょう。(自戒)
guest

0

vector<int> a(N); をソートしたのなら、逐次比較しつつ勘定する必要はないのでは?
※ なんのためにソートしたんです?

a が昇順にソートされているなら、
任意の身長xに対し x <= a[t] なる最小の要素:a[t]を見付ければ、
そこから aの末尾まで の距離(人数)が求める答ですよね?

[追記] やってみた:

C++

1#include <iostream> 2#include <algorithm> 3#include <vector> 4#include <iterator> 5 6int main() { 7 using namespace std; 8 9 int N, Q; 10 cin >> N >> Q; 11 12 vector<int> a(N); 13 vector<int> x(Q); 14 15 for ( int& item : a ) cin >> item; 16 for ( int& item : x ) cin >> item; 17 18 sort(a.begin(), a.end()); 19 for (int i=0; i<Q; i++) { 20 cout << distance(lower_bound(a.begin(), a.end(), x.at(i)), a.end()) << endl; 21 } 22}

投稿2021/12/19 13:09

編集2021/12/20 00:02
episteme

総合スコア16614

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

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

Kaguya_4869

2021/12/19 23:20

episteme様 昨日の多次元配列の質問に続き、こちらにも回答してくださり、 ありがとうございます。 確かに、私のコードですと、ソートしたにも関わらず、 それを活かせていませんでした。 上の方々のコメントや回答から、 x<=a[t]になる最小の要素a[t]を見つけるには、 二分探索が必要そうだということでしたので、 それを使ってみようと思います。 ありがとうございます。
episteme

2021/12/20 00:02

追記しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問