リブlab

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

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

 このシリーズ第一回

hajimekata-nyumon-donyu.hatenablog.com

 前回

hajimekata-nyumon-donyu.hatenablog.com

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

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

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

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

 ・ABC083_B

問)1以上N以下の整数のうち、10進法での各桁の和がA以上B以下であるものの総和を求めてください。

提出 #4549595より

int main(){
    int n,a,b;
    cin>>n>>a>>b;
    int ans=0;
    for(int i=1;i<=n;++i){
        string s=to_string(i);
        int sum=0;
        for(int j=0;j<s.size();++j){
            sum+=s[j]-'0';
        }
        if(a<=sum&&sum<=b)ans+=i;
    }
    cout<<ans<<endl;
}

 これは1以上N以下の範囲で1,2,3,4,...,25,...,nのそれぞれに対して文字列に変換し、その変換した文字列の各桁の和をsum+=s[j]-'0';によって求めている。

 s[j] - '0' は何を表しているのでしょうか。まずs[j] を確認しましょう。s は与えられた整数の文字列です。s[j] はその文字列のj 番目の文字でした。つまりs[j] は0~9のどれかが文字として出力されることになります。

 ということは、例えば '9' -'0' のような計算になるということです。では、その計算結果はどうなるのでしょうか。答えは9になります。'数字' - '数字' をするとその数字が後ろの数字からどれだけ離れているかを出力することになります。'5' - '2' であれば '3' です。

 これで、sum += s[j] - '0' の意味が分かりましたね。s[j] の文字列になっている数字をint型として出力してくれるようになっています。
 そして、各桁の和を求めるたびに範囲を確認して条件を満たす数字の数を数えているんですね。
 最後にもう一度、先ほどのコードを見てみましょう。理解できるのではないでしょうか。

int main(){
    int n,a,b;
    cin>>n>>a>>b;
    int ans=0;
    for(int i=1;i<=n;++i){
        string s=to_string(i);
        int sum=0;
        for(int j=0;j<s.size();++j){
            sum+=s[j]-'0';        // 123なら sum = 1+2+3;
        }
        if(a<=sum&&sum<=b)ans+=i;
    }
    cout<<ans<<endl;
}
 Tips02_to_string()は数字を文字列に変換する関数

 もちろん、逆もある。stoi( )だ。(恐らく、string_to_intの意)
これらはそれぞれ、to_string('数字'), stoi('文字')のように書く。
to_string()もstoi()もそれぞれ引数を変換した結果を戻り値として返してくれる。

ちなみに自分の解答は

signed main()
{
  int n,a,b;
  cin >> n >> a >> b;
  int ans = 0;
  
  for(int i = 1; i <= n; ++i){
   int sum = 0;
    int x = i;
    for(int j = 0; j < 5; ++j){//たかだか5桁(10000)なので五回
      sum += x % 10; //一の位が求まる。
      x /= 10;  //139 becomes 13 
    }
      if(a <= sum && sum <= b)
        ans += i;
  }  
  cout << ans;

  return 0;
}

 整数の10進法の表記を考えて答案しました。処理の流れはコメントに書いてあるので恐らく大丈夫だと思います。

 このように、10進法を考えてやれば整数を一桁一桁考えてやることができます。 まず、10で割ったあまりを考えることで整数の1の位が何なのかを取得します。

x % 10 → x の一桁目
:
  1 % 10 → 1 なぜなら  1 = 0×10 +1
  2 % 10 → 2 なぜなら  2 = 0×10 +2
 34 % 10 → 4 なぜなら 34 = 3×10 +4
456 % 10 → 6 なぜなら456 = 45×10 + 6

次に、それを何らかの処理(今回ではsum+=)をしてやった後に、x /= 10;によって 一桁ずらしています。(ずらした分は消えます。)後は範囲を満たしていれば出力するだけです!

(あれ?これけんちょんさんと同じアルゴリズムじゃね?まぁ、そりゃけんちょんさんの記事で勉強してるからそうなるけど。
まぁ他の方の解答だけだったら怒られるから自分の解答を必ず付けることにしてるから仕方がないっちゃ仕方がないけど解説も似たようなもんじゃん...と思ったけどまぁ2回見ることによってより理解深まるよね。あと、これ日記だからセーフだから(迫真)あと、僕の備忘録というか理解のためでもあるし...)

さいごに

 今回はto_string()によって数字を文字列にする方法を学びました。さらに、整数の10進法表記の処理として、一桁ずつ処理をするなら%10をすることで最下位桁を着目することができ、元の整数を/=10することで着目している桁を一つ左にずらせることを学べたのではないでしょうか。
 以上!ABC 102とABC113の解説でしたー。