LoginSignup
11
9

More than 3 years have passed since last update.

競技プログラミング初心者のためのTips

Last updated at Posted at 2019-08-31

この記事は北九州高専コンピュータ研究部の2019年度の一年生の為に書かれたものです.
弊部の教育システムではしっかり教えて入れないのにも関わらず競技プログラミングの問題を解く上で必要な知識を一部ですがまとめました.
独学で競技プログラミングを学んでいた人にとっては常識的なことばかりを書いているかもしれないですがご容赦ください.

cin/cout の高速化

cin, cout は一般に同じ入出力関数である scanf, printf と比較すると遅いと言われています.
その解決方法は以下のように main関数 に記載することで高速化されます.

#include <bits/stdc++.h>
using namespace std;

int main() {
  // ↓この2行が追加部分
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);

  cout << "Hello World!" << endl; 
}

そもそもなぜこれにより cin,cout が高速化されるのかという話ですが,
cinはデフォルトでcoutと紐づいています. これによりプログラムで処理を行う際にデータを一時的に格納するためのメモリ領域であるバッファーと呼ばれる領域内に貯められたデータを吐き出すといった処理をcinを呼び出す度に行われています.
そこで上記の2行でcincoutの関係性を切り離すという処理を行い入出力を高速化しています.

ただ,自分で経験した範囲ではこの記載がないとC++では解くことができない問題というものに遭遇したことはないので不要な知識かもしれないですが頭にとどめておいても良いかもしれません.

vector,stringの要素アクセス

普段at関数を用いることで配列の要素にアクセスしています.
これは[]を使うことで各要素にアクセスすることが可能です.

#include <bits/stdc++.h>
using namespace std;

int main() {
  vector<int> vec = {1,2,3,4,5};
  string str = "abcde";

  // この2行は同じ結果
  cin >> vec.at(0);
  cin >> vec[0];

  // この2行は同じ結果
  cout << vec.at(0) << endl;
  cout << vec[0] << endl;

  // この2行は同じ結果
  cin >> vec.at(0); //->1
  cin >> vec[0]; //->1

  // この2行は同じ結果
  cout << str.at(0) << endl; //->a
  cout << str[0] << endl; //->a
}



多次元配列にアクセスする際は以下のようになります.

#include <bits/stdc++.h>
using namespace std;

int main() {
  vector<vector<int>> vec(4, vector<int>(4));

  // at関数を使用したパターン
  for(int i=0;i<4;i++){
    for(int j=0;j<4;j++){
      cin >> vec.at(i).at(j);
    }
  }
  for(int i=0;i<4;i++){
    for(int j=0;j<4;j++){
      cout << vec.at(i).at(j) << " ";
    }
    cout << endl;
  }

  // []を使用したパターン
  for(int i=0;i<4;i++){
    for(int j=0;j<4;j++){
      cin >> vec[i][j];
    }
  }
  for(int i=0;i<4;i++){
    for(int j=0;j<4;j++){
      cout << vec[i][j] << " ";
    }
    cout << endl;
  }
}

基本的にat関数と[]の挙動は基本的には同じですが以下のような違いがあります.

at関数の場合は範囲外を参照したときにエラー(std::out_of_range)になりますが、
[]の場合は普通に配列外参照をし、エラーになりません。

そのため、[]の場合はインデックスの指定をミスしてもとりあえず動き、謎の値が返ってきます。

配列

vectorと似たような機能をもつデータ構造です.

宣言方法は以下のような方法です

型 変数名[要素数];

実際の使用例は以下のようです.

#include <bits/stdc++.h>
using namespace std;

int main() {
  int n;
  cin >> n;
  int a[10];
  for(int i=1;i<=n;i++)a[i-1] = i*i;
  for(int i=n;i>=1;i--)cout << a[i-1] << " ";
  cout << endl;
}

注意点として.at().size()といった関数は存在しないためvectorより不便に感じることは多いかもしれません.
ちなみに多次元配列を利用する際は以下のようになります.

#include <bits/stdc++.h>
using namespace std;

int main() {
  int n;
  cin >> n;
  int a[n][n];
  for(int i=1;i<=n;i++){
    for(int j=1;j<=n;j++){
      a[i-1][j-1] = i*j;
    }
  }
  for(int i=n-1;i>=0;i--){
    for(int j=n-1;j>=0;j--){
      cout << a[i][j] << " ";
    }
    cout << endl;
  }
}

各種参考文献はvectorではなくこちらの配列を使用することも少なくないので頭に入れておくことをお勧めします.

キャスト変換 と 桁数指定

弊部の講義の最終到達地点の範囲外ですがAPG4bの3章の一番最初を読んで見ましょう.
https://atcoder.jp/contests/apg4b/tasks/APG4b_y

200点問題以降必要になる可能性がある知識なので確認するようにしてください.

ASCIIコード

char型は数字として扱うことが可能です.

そのためchar型をint型にキャスト変換をすると以下のようになる.

#include <bits/stdc++.h>
using namespace std;

int main() {
  char a,b,c,d;
  a = '0';
  b = '1';
  c = 'A';
  d = 'a';

  //int型にキャスト変換する
  cout << (int)a << endl; //-> 48と出力
  cout << (int)b << endl; //-> 49と出力
  cout << (int)c << endl; //-> 65と出力
  cout << (int)d << endl; //-> 97と出力
}

次は逆に逆にint型をchar型にキャスト変換する.

#include <bits/stdc++.h>
using namespace std;

int main() {
  char a,b,c,d;
  a = 48;
  b = 50;
  c = 65;
  d = 100;

  //int型にキャスト変換する
  cout << (char)a << endl; //-> 0と出力
  cout << (char)b << endl; //-> 2と出力
  cout << (char)c << endl; //-> Aと出力
  cout << (char)d << endl; //-> dと出力
}

このようにint型の数値と文字列は1:1で対応している.
その対応表は以下のサイトが参考になります.
https://qiita.com/disapalt/items/7c816f8460c3969662f6

これを利用することで以下のようなことができたります.

#include <bits/stdc++.h>
using namespace std;

int main() {
  string s;
  cin >> s;
  cout >> "> ";
  for(int i=0;i<s.size();i++){
    if(65 <= s.at(i) && s.at(i) <= 90){ // この範囲は大文字が格納されている
      cout << (char)(s.at(i)+32); // 大文字を小文字に変換
    }else if(97 <= s.at(i) && s.at(i) <= 122){ // この範囲は小文字が格納されている
      cout << (char)(s.at(i)-32); // 小文字を大文字に変換
    }else {
      cout << s.at(i);
    }
  }
  cout << endl;
}
$ ./test
abcDEF100
> ABCdef100

このように大文字小文字を数字を32だけずらすことで変換できたりします.

long long int 型

int型は32bit変数です.
こんなの言われても「は?」となると思いますが簡単にいうと
最低値が $-2^{31}=-2147483648$ で最大値が $2^{31}=2147483647$
ということです.
Atcoderの300点以上の問題では最大値最小値がそれを超える場合があります.
その対策として long long int 型を使用するというものがあります.
これは64bit変数と呼ばれるもので
最小値が $-2^{63} = -9223372036854775808 $
最大値が $2^{63} = 9223372036854775807 $
という変数です.

#include <bits/stdc++.h>
using namespace std;

int main() {
  int a = 9223372036854775807;
  cout << a << endl;
  long long lla = 9223372036854775807;
  cout << lla << endl;
}

これを実行すると

$ ./test
-1
9223372036854775807

範囲外に収りきっていない場合は違う値が帰ってくれるのでこれがバグの原因になったりするのでメモリに余裕のある場合はできる限り long long を使用する習慣を意識づけても良いかもしれません.

math

APG4bに記載されていないですがよく使う関数が色々あるので記載

abs

引数の絶対値を返す関数です.二変数の差を求める際によく使います.

cout >> abs(100) >> endl; // 100
cout >> abs(-120) >> endl; // 120
cout >> abs(100-120) >> endl; //20

pow

pow(a,b)の場合, $a^b$ を返します.

#include <bits/stdc++.h>
using namespace std;

int main() {
   double x, y, z;

   x = pow(4,2);
   y = pow(2,3);
   z = pow(2,0.5);
   cout << x << endl; // 16
   cout << y << endl; // 8
   cout << z << endl; // 1.41421
}

参考文献

11
9
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9