リブlab

理系大学生のIT・電気・大学・趣味についての日記

AtCoderの良解答からみるプログラミングのテクニック02(ビギナー向け) C++

前回の記事はこちら

drken(けんちょん)さんの記事の進行で進みます。

 こんにちは、今回も学んだことを共有させていただきます。
 途中に他の方のコードを引用しますが、そのコードの著作権はそのコードを書いた方が有しています。
 元のコードが見たい場合は問題名 提出#〇〇〇〇〇で検索をすれば見ることができます。
 今回から第九回までは提出#〇〇〇〇〇のかたちで紹介させていただきます。が十回目からはリンクを張ることにしました。検索してもでず、地道に番号から探さないと見つけられないみたいだからです。

※注 スマホだとコードは見にくいのでPCモードかPCで見たほうが良いです。

At Corder で見つけた良解答から得たもの

 ABC081_A・・・与えられた1と0のみで構成された整数の中に1は何回出現しましたか?

提出 #4349748を見ると

int main() {
  int a ;
  cin >> a ;
  cout << a%2 + a/100 + (a/10)%2 << endl;
}

 のように解答しています。これ面白いですよね。一応自分も除算をする選択肢は持っていたのですが、それよりもstringで処理したほうが良いと判断してstringで処理しました。ですのでこれを見た時よく除算でやりきったなぁと思いました。
 それはさておき、解説に入りましょう。

 これは、一の位がa%2、十の位が(a/10)%2そして、百の位がa/100と対応していると思います。そして、それぞれの計算結果は必ず1か0になります。この計算結果はもちろん、ある位は1なのか0なのかを表しています。最後に全ての位を足すことで1が整数の中に出現した回数を出力することができています。面白いですね。

 Tips01_後置演算子は必要な時だけ使用する。

 ++iとi++のような形でよく使われるインクリメント演算子。これは後置よりも前置で使うことがC++では推奨されています。
 なぜかというと後置では一度iをコピーしてからインクリメントされていない値を結果として返す必要があるからです。そのため、前置と比べて処理が増えるからです。もちろん必要な時は後置を使いましょう。

 Tips02_配列の添え字を定義するときには、size_t型を使いましょう。

 c言語だとfor(int i = 0; ・・・・)のように書けますが(c99とかだとね)、c++だとfor(size_t i = 0; ・・・・)が推奨されてます。


 ABC081_B

 これの僕の解答はこんな感じです。

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

signed main()
{
  int a; cin >> a;
  int n;
  int cnt = 0;
  bool flag = false;
  vector<int> v1(0);
  while(cin >> n)
  v1.PB(n);
  
  while(!flag){
   for(const auto& i: v1){
    if(i%2 != 0)
     flag = true;
   }
  
   if(flag != true){
/* 
     for(auto& i: v1)
       i /=  2;
*/
    ++cnt; 
   }
  }

  /*
  for(const auto& i: v1)
  cout << i;
 */
 
  cout << cnt;
  
  return 0;
}

 C++ですよ!って感じしますよね。なんかfor文は極力範囲for文で書いて、配列じゃなくてvectorを使ってやるぜという意気込みを感じます。

 はい、解説しますと全部が偶数であるかをwhile文中で調べて割り切れないときにflagをtrueにします。次に、全てが偶数の時にcnt(よくあるカウンタ変数)をインクリメントし、インクリメントした回数を出力するという仕組みです。

 で、模範解答を見ると、与えられた整数それぞれが最大で何回 2 で割れるかを求めてその最小値を求めるというプログラムを書いているのを見ました。初めて見た時はああ、こんな解き方もあるのかと感銘を受けました。

おわり

 以上!ABC 081のAとBの解説でしたー。

次回はここをクリック