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

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

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

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

Q&A

解決済

3回答

455閲覧

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

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

0クリップ

投稿2018/04/26 00:44

前提・実現したいこと

この問題を解きたいです。
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); ^ と表示されました。

該当のソースコード

C

1 2#include<stdio.h> 3#include<math.h> 4 5int main(void){ 6 7 int n=0, a=0, b=0; 8 int i=0; 9 int k=0; 10 int num[10000]={0};//1からnまでの整数を格納する配列 11 int j=0;//num[]の添字 12 int num_q[5]={0};//各numの各桁の値を保存する 13 /*下記の計算方法で割り算を行うと10^kから10^0まで割り算を 14 するから、回数はk-0+1でk+1回。いま<10^4つまり5桁までの数字という 15 制約があるから添字は*/ 16 int num_q_sum=0;//各numの桁数の和 17 int all_sum=0;//桁の和がa以上b以下を満したnumを足す 18 int back_num=0; 19 int back_k=0; 20 21 scanf("%d %d %d", &n, &a, &b); 22 23 //1からnまでの整数を格納した配列をつくる 24 for(i=0; i<n; i++){ 25 num[i]=i+1; 26 } 27 28 //それぞれの整数について、桁の和が条件を満すかを確認するためのfor文 29 for(j=0; j<n; j++){ 30 //それぞれの整数10の何乗で割るかをきめるためにやるやつで 31 //例えばnum[j]=11のとき、11<10^2よりk=1 32 for(i=0; i<5; i++){ 33 if( (float)num[j] < powf(10, (float)i) ){ 34 k=i-1; 35 break; 36 } 37 } 38 39 back_num=num[j]; 40 i=0; 41 back_k=k; 42 /*num[j]を10のk乗で割った商(num_q)はnum[j]の最上位の数字、 43 num[j]-num_qを10のk-1乗で割った商はnum[j]の上から二桁目の数字、 44 というのを利用して一回ごとに配列num_qに各桁の値を保存するためのwhile文*/ 45 while(k>=0){ 46 num_q[i] = num[j] / powf(10.0, (float)k); 47 num[j] = num[j] - num_q[i] * powf(10.0, (float)k); 48 i++; 49 k--; 50 } 51 //num[j]が0になるので上でback_numにcopyしておいてall_sumを 52 //求めるときに使う 53 54 //num[j]の各けたを足し算する 55 for(i=0; i<back_k+1; i++){ 56 num_q_sum=num_q_sum + num_q[i]; 57 } 58 59 if(num_q_sum>=a && num_q_sum<=b){ 60 all_sum=all_sum+back_num; 61 } 62 63 num_q_sum=0; 64 } 65 66 printf("%d\n", all_sum); 67 68 return 0; 69} 70

試したこと

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

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

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

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

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

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

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

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

guest

回答3

0

ベストアンサー

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 03:05

編集2018/04/29 02:09
KSwordOfHaste

総合スコア18394

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

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

退会済みユーザー

退会済みユーザー

2018/04/26 09:45

やります…
退会済みユーザー

退会済みユーザー

2018/04/29 00:12

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

2018/04/29 00:56 編集

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

2018/04/29 01:36

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

退会済みユーザー

2018/04/29 02:43

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

2018/04/29 03:10

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

0

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

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

投稿2018/04/26 00:54

atmn3356

総合スコア157

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

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

退会済みユーザー

退会済みユーザー

2018/04/26 09:48

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

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 01:56

coco_bauer

総合スコア6915

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

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

退会済みユーザー

退会済みユーザー

2018/04/26 09:46

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問