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

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

ただいまの
回答率

90.47%

  • C

    3818questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

数字の各桁の和が制約に合うのを見つけてその数字の和を足したい

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 215
退会済みユーザー

退会済みユーザー

 前提・実現したいこと

この問題を解きたいです。
https://abs.contest.atcoder.jp/tasks/abc083_b
At CoderではCのGCC5.4.1を選択しました。
ですが使ってるのをgcc--versionで確認すると4.8.5と出ましたが
これって大丈夫なんでしょうか。。

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

上のページの入出力例は成功しましたが
AtCoderに提出するといくつかのtest caseでWAと出ます。
また、コンパイルメッセージに
./Main.c: In function ‘main’:
./Main.c:21:3: warning: ignoring return value of ‘scanf’, declared with attribute warn_unused_result [-Wunused-result]
scanf("%d %d %d", &n, &a, &b);
^
と表示されました。

 該当のソースコード

#include<stdio.h>
#include<math.h>

int main(void){

  int n=0,  a=0, b=0;
  int i=0; 
  int k=0;
  int num[10000]={0};//1からnまでの整数を格納する配列
  int j=0;//num[]の添字
  int num_q[5]={0};//各numの各桁の値を保存する
  /*下記の計算方法で割り算を行うと10^kから10^0まで割り算を
    するから、回数はk-0+1でk+1回。いま<10^4つまり5桁までの数字という
    制約があるから添字は*/
  int num_q_sum=0;//各numの桁数の和
  int all_sum=0;//桁の和がa以上b以下を満したnumを足す
  int back_num=0;
  int back_k=0;

  scanf("%d %d %d", &n, &a, &b);

  //1からnまでの整数を格納した配列をつくる
  for(i=0; i<n; i++){
    num[i]=i+1;
  }

  //それぞれの整数について、桁の和が条件を満すかを確認するためのfor文
  for(j=0; j<n; j++){
    //それぞれの整数10の何乗で割るかをきめるためにやるやつで
    //例えばnum[j]=11のとき、11<10^2よりk=1
    for(i=0; i<5; i++){
      if( (float)num[j] < powf(10, (float)i) ){
    k=i-1;
    break;
      }
    }

    back_num=num[j];
    i=0;
    back_k=k;
    /*num[j]を10のk乗で割った商(num_q)はnum[j]の最上位の数字、
      num[j]-num_qを10のk-1乗で割った商はnum[j]の上から二桁目の数字、
      というのを利用して一回ごとに配列num_qに各桁の値を保存するためのwhile文*/
    while(k>=0){
      num_q[i] = num[j] / powf(10.0, (float)k);
      num[j] = num[j] - num_q[i] * powf(10.0, (float)k);
      i++;
      k--;
    }
    //num[j]が0になるので上でback_numにcopyしておいてall_sumを
    //求めるときに使う

    //num[j]の各けたを足し算する
    for(i=0; i<back_k+1; i++){
      num_q_sum=num_q_sum + num_q[i];
    }

    if(num_q_sum>=a && num_q_sum<=b){
      all_sum=all_sum+back_num;
    }

    num_q_sum=0;
  }

  printf("%d\n", all_sum);

  return 0;
}

 試したこと

実行しても希望どおりになるので何も試せませんでした。

 補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+3

WAという判定は「誤答」という意味です。

つまり正しくない答えが出るケースがあるのは確かです。コンパイル結果にwarningがあっても結果が正しければWAにはならないと思います。

質問者さんの修行の邪魔になるといけないので具体的なエラーケースについては言及を控えますが・・・最初にやってみたケースで見つかりました。


追記:質問者さんが降参して正解回答をごらんになったとのことで本件のWAの原因についてコメントします。
自分が見つけた問題は「調べる数が10000のとき、それが何ケタかを求める論理の間違い」です。調べる数が1~9999までの値の場合は桁数kが正しく求まりますが10000のとき、
if( (float)num[j] < powf(10, (float)i) ){
この行の条件が成立しません。この行はforループの中にありますが、iは0~4まで変化しますので不等号の右辺は1, 10, ..., 10000と変化しますが<が成立しませんよね?そのため条件が成立した際の処理
k = i - 1が実行されずループ完了時点でkは-1になります。これが原因で以降の論理が破綻します。

デバッグの際のコツですが、論理を確認しても間違いが見つからない場合、まずNGとなる条件を見つけることで突破できることがあります。また多くのNGケースは「境界ケース」です。本件についていえば自分が最初にテストしたのはたまたま境界ケースである「10000 1 36」でした。範囲内の全ての値についてテストがパスしなければならないケースですね。実際その入力でテストしたところ49995000という結果になったため一目で「これはどこかにバグがある」と分かりました。というか本来の値50005000とちょうど10000の差があるので「おそらく10000のケースがまずいのだろう」と想像できました。しかしここで「10000がまずい」と決めつけず「本当に10000がテストをパスしなかったのか」を確認した方がよいだろうと思いました。デバッグの際には「事実を一段階ずつ確実につかむ」のが鉄則です。確認せぬまま推測で先に進めることもありますが万一その推測が間違っていると時間を大分無駄にしてしまいます。10000がNGかどうかはループの中でall_sumに加算している部分でデバッグプリントしてみれば一発でわかります。実際確認してみると確かに10000が加算されてないことが判明しました。
ここまでくると原因究明はほぼできたも同然です。10000の場合のみ各論理がどのように動作するか集中してよーく見てみればいいのです。もしそれでもわからなかったらデバッグプリントを入れて「ループが何回まわるか」「各if文の判定結果は期待通りか」「変数の値がポイントポイントで期待どおりの値か」をつぶさにデバッグプリントで確認すれば、コードを目で見てあれこれ考えなくても「事実」が簡単につかめます。

コードを目で眺めただけで経験豊富なプログラマーであれば「バグに気づく」ことはできます。しかしそれは初心者には難しいです。初心者の方は「実際に動かして変数の値やプログラムが走行した道筋がどうなのかの事実を調べる」のがお勧めです。

(なお、本件に直接関係しませんが、本問題に浮動小数点演算は余計であると感じるプログラマーは多いと思います。整数演算で充分と思います。)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/26 18:45

    やります…

    キャンセル

  • 2018/04/29 09:12

    おはようございます。分からなくて解答を見ると、if(sum==y)でi2,j2,n-i2-j2をそれぞれ別の変数に格納していて、実際やってみると通ったんですが、それをしなくても僕とやってることは同じはずなのにどうして僕のは通らないんでしょうか。

    キャンセル

  • 2018/04/29 09:54 編集

    > 「どうして僕のは通らないんでしょうか。」
    ご自分の質問内容をごらんください。この質問を発した時点での質問者さんの認識と疑問点の焦点(なぜwarningがでるのか)が既に変化していますよね?尋ねたいことが変化したなら新たに質問者さんが考えたことを明記して別の質問を挙げるべきではないかと自分は思います。
    「なぜwarningがでるか」については他の方々が回答しておられますよ?
    --追記--
    質問は「やっている問題がOKになること」がクローズの条件ではないです。発した質問に対する「妥当な回答がついて、それが明らかになったことをもって」クローズすべきと自分は思います。

    キャンセル

  • 2018/04/29 10:36

    ・・・とコメントしたんですが、それなら自分もこのような回答をつけるべきではなかったですね。中途半端な主張してしまってスミマセンでした。
    WAの原因について回答してみますね。

    キャンセル

  • 2018/04/29 11:43

    分かりやすく丁寧な回答ありがとうございます。境界線に注目するというのはすごく身になりました。あと、たしかにpowf関数を使わなくても10で割っていけばもっときれいですよね。。

    キャンセル

  • 2018/04/29 12:10

    きれいと言うより・・・powfは実数どうしのべきを演算するのでもし使うとしたら浮動小数点数の誤差について「問題ない」ことに確信をもっておくべきです。本件においては実際上は問題ないのですが、コードを拝見したときまず「浮動小数点数の誤差を気にしていないのではないか」と感じました。そんな難しい点を意識せずとも整数演算にすると「論理が正しいことが明快になる」と思います。

    キャンセル

+1

「scanf()の戻り値を使っていないので注意してください」という警告文です。
戻り値を使う形にするか、警告を抑制するかが必要です。

古いサイトですが、こちらのページが分かりやすいです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/26 18:48

    scanfに戻り値なんてあったんですね(いつも何個指定するか分かった状態で書いていたからエラーなんて出さなかったし気づきませんでした )。サイトの紹介ありがとうございました。

    キャンセル

0

scanfは、値を取り込む先(第2引数以降でポインタを指定した変数)のどこまでにデータを入れられたかを返します。

scanf("%d %d %d", &n, &a, &b); の返り値が1だとa,bに値が入っていない、の返り値が2だとbに値が入っていないという事になります。
aやbに値が入れられていない(値は0のまま)だと、それ以降のプログラムが思ったように動かない可能性があるので、Warningが出たのです。

scanf("%d %d %d", &n, &a, &b); の返り値が3かどうかを調べて、3以外なら再度scanfを実行するようにすれば、問題は解決します。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/04/26 18:46

    scanfの戻り値なんて意識したことなかったですけど関数だからあって当たり前でしたね。。ありがとうございます 。

    キャンセル

関連した質問

同じタグがついた質問を見る

  • C

    3818questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。