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

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

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

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

Q&A

解決済

4回答

3071閲覧

敵を倒せるか、倒せないのか判断するシステムを作りたい

tssubaru

総合スコア1

C

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

0グッド

0クリップ

投稿2023/01/26 04:02

前提

プログラミングの勉強をはじめ、一週間ほど経った初心者の者です。
参考書を用いて勉強しているのですが、演習の問題で分からないものがあります。

参考書の問題はこうです。

「入力値を標準出力から受け取り、答えを標準出力に表示するシステムを作りましょう。」

あなたは今シンプルなRPGゲームを遊んでいるとします。
敵のHPは「h」で1ターンごとに以下のように進みます。

・あなたが敵を攻撃したら、敵のHPは「a」減ります。
・敵のHPが0以下になったら倒したことになります。
・敵は回復をするため、敵のHPは「b」増えます。

もし倒せるならば、1行目に「YES」を出力し、次の行にかかったターン数を出力して下さい。
倒せないならば、「NO」を出力して下さい。

(入力ルール)

h a b
1≤h≤10の18乗,整数
1≤a≤10の18乗,整数
1≤b≤10の18乗,整数

(出力ルール)

YES/NO
[n]

実現したいこと

自分なりに考えてコードを書いてみたのですが、思うようにいきませんでした、、
模範解答が載っていなかったので、出来れば模範解答を頂きたいです、、

自分でかいてみたコード

#include <stdio.h> #include <stdlib.h> int main(void) { int h; int a; int b; int n; int h1 = h-a; int h2 = h1+b; for(n=0; 0<h2; ++n){ scanf("%d", &h); scanf("%d", &a); scanf("%d", &b); } if(0>h2){ printf("Yes \n"); printf("%d", n); }else printf("NO"); return 0; }

実行してみた結果

Yes 0line[1]:

試したこと

まず、変数h1として、敵の体力(h)から自分からの攻撃の分(a)を引いた数として代入しました。
次に変数h2として、その引いた数(h1)に敵の回復した分(b)を足した数として代入しました。

そして次に、敵のHPが無くなるまでプログラムを繰り返すためにfor関数を用いてコードを書きました。
ここでの(n)は敵を倒すのにかかったターン数の変数として宣言し、インクリメントを用いて、その(n)を繰り返した分だけ加算していくようにしました。
forの式の真ん中の、(0<h2)は最終的の敵のHPをh2としているため、0より敵の体力が多かったら繰り返す、という意味を込めてこのコードを書きました。

そして最後に、もし敵の体力が0より小さくなったら、YESと、変数を(n)として置いたターン数を出力するようにし、
そうでなければ、NOを出力するようにしました。

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

自分でもなぜうまくいかないのか考えてみたのですが、考えていくうちに頭がごちゃごちゃしてきて途方に暮れてしまいました。
もし宜しければお手数ですが、模範解答と、何がいけないのか教えて頂けると幸いです。

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

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

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

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

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

Zuishin

2023/01/26 04:28 編集

問題文がここに掲載されている分しかないのであれば、攻撃力の方が敵の回復量より大きいまたは HP 以上であれば倒せるしそうでなければ倒せないのでは? 倒せない場合無限ループになるので、ループしてはいけません。倒せるかどうかの判定は真っ先に行い、ターン数は除算で出ます。
dodox86

2023/01/26 04:26

途方に暮れるようでは、この問題に取り組むのが早過ぎるのでは。変数のh1、h2とも、まだaやbの値が確定する前に代入してしまっていますよね。コードを書く前に頭の中でフローを考えましょう。
tssubaru

2023/01/26 05:43

そうでした、、 h1とh2の変数を使用するのであれば、scanf関数の後に配置すべきということでしょうか
fana

2023/01/26 06:05

> 10の18乗 ナチュラルに int h; とかしてるけど, 環境でのint型のサイズ次第では問題になりそうな気がします. (そんな細かいことに取り合う段階にはなさそうですが)
tssubaru

2023/01/26 07:32

今気が付いたのですが、 ×入力値を標準出力から受け取り、答えを標準出力に表示するシステムを作りましょう。 〇入力値を標準入力から受け取り、答えを標準出力に表示するシステムを作りましょう。 でした。
tssubaru

2023/01/26 08:31

コンパイルについて追記しておきます。 clangのgccコマンドを使用しています。
guest

回答4

0

(質問へのコメント欄にも書きましたが)
入力値の範囲的に int 型で大丈夫なのか? という話はありますが,
とりあえずそれは置いとくとして.

C

1//※とりあえずint型で大丈夫なのだという仮定の下で. 2int main( void ) 3{ 4 int h,a,b; 5 scanf( "%d %d %d", &h,&a,&b ); 6 7 if( h<=a ) //初撃で倒せるならbの値は無関係 8 { printf( "YES\n1\n" ); } 9 else if( a <= b ) //回復量を上回る攻撃ができないなら倒せない 10 { printf( "NO\n" ); } 11 else 12 { //ループでターン数で求めるならこんな感じかな 13 int nTurn = 1; 14 while( h>a ) 15 { 16 h += (b-a); 17 ++nTurn; 18 } 19 printf( "YES\n%d\n", nTurn ); 20 } 21 22 return 0; 23}

↑はターン数を求めるところをループで書いてますが,
計算して出すなら該当箇所を以下のようにすれば良いかと思います.

C

1... 2 else 3 { 4 //ループじゃなくて計算で出すならこんな感じかな 5 int nTurn = 1 + (h-b-1) / (a-b); 6 printf( "YES\n%d\n", nTurn ); 7 } 8...

投稿2023/01/26 07:03

fana

総合スコア11654

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

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

fana

2023/01/26 07:09

ターン数の計算 > int nTurn = 1 + (h-b-1) / (a-b); について: あるターンで h<=a ならばそのターンの攻撃で倒せる. つまり,必要ターン数は ( h<=a な状態に持ち込むのに必要なターン数 ) + 1 である. > ( h<=a な状態に持ち込むのに必要なターン数 ) というのは ( h-a ) / ( a-b ) ではなくて, ( h-a + (a-b-1) ) / ( a-b ) である. これの分子をまとめると ( h-b-1 )/(a-b) となる.
tssubaru

2023/01/26 08:25

なるほど、、わざわざループを使う必要はなかったのですね。 計算でターンを求める方が処理も少なく済むように感じました。
tssubaru

2023/01/26 08:29

こちらのコードを使用させて頂き、実行してみたのですが、 どの回答にも謎に、 YES 1 line[1]: や NO line[1]: といったように余計なline[1]: が出力されるのですが、これは何故でしょう? これが最初に指摘して頂いた、サイズの問題が影響してくるのでしょうか、、
tssubaru

2023/01/26 08:49

#include <stdio.h> #include <stdlib.h> // 問題の要求する1行のサイズが大きい場合はこの値を変更してください。 // If the problem requires more large line size, please modify following. #define LINE_BUF_SIZE 1024 int main( void ) { static char line[LINE_BUF_SIZE]; int index = 1; while (fgets(line, sizeof(line), stdin) != NULL) { printf("line[%d]: %s", index++, line); } return 0; ややこしくなると思い、質問の方には載せなかったのですが、 参考書に付属していた、プログラムを試しに書く事の出来る専用のサイトのようなものがあり、 あらかじめ、defineやindexといったコード(ここはまだ学べておりません、、)が書かれており、 int main( void ) { から僕自身のコードを入力していった形です。 原因はこれでしょうか。
fana

2023/01/26 10:17

よくわからんですが,今回使わないコードが残っているということなのであればそれは除去するべきでしょう.
fana

2023/01/27 02:25 編集

> なるほど、、わざわざループを使う必要はなかったのですね。 > 計算でターンを求める方が処理も少なく済むように感じました。 扱う値が小さいならば,ループでも問題ないとは思います. 今回,問題文で示されている入力値の値域は,暗に「ループでちまちまやってると時間の面で問題になり得るっていう話だぞ」と言ってるんじゃないかな,と思います. (h=10の18乗,a=2, b=1 なんて入力があり得るわけで,この場合だと 10の18乗回くらいループ回さないとならない!)
guest

0

ベストアンサー

まずルールから何が必要なのかを考えてみましょう。

まずゴール。これは

・敵のHPが0以下になったら倒したことになります。

つまり、敵のHPを0にすることです。
じゃあ敵のHPをゼロにする条件は何でしょうか?この時点ではターン数については考えないでください。

はい。一撃で倒す回復より大きいダメージで削りきるかの二つですね。
前者はa>=h,後者はa>bですね。
つまり、このどちらかであれば倒せる。違うなら倒せないということが先に分かります。
先にここでYESかNOが確定します。

ではその次、経過ターン数です。これについては倒せる時点でターン数は計算で求まります。(自分の計算だと合ってなかったので他の人の解答見てください)
以上を踏まえてコードを考えてみましょう。なんとif文1つだけで書けちゃうということが分かります。

回答載せてましたが誤答だったので消しますね。他の人が正答載せてるのでそちら参照してください。本音いうと回答ごと消したかったけどBAにされてるままだと不誠実なので

投稿2023/01/26 05:25

編集2023/01/26 23:55
pig_vba

総合スコア807

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

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

Zuishin

2023/01/26 05:50 編集

模範解答はもう一つの罠にかかっているようです。 実は一撃で倒せるかどうかは場合分けする必要ありません。
tssubaru

2023/01/26 05:50

なるほど、、私は複雑に考え過ぎていたのですかね、、 そもそも繰り返しの関数を使う事自体間違いだったのでしょうか、、
pig_vba

2023/01/26 06:32

>実は一撃で倒せるかどうかは場合分けする必要ありません。 その場合の単一多項式が浮かばなかったんでこうしました。 まぁwhile使っていいならif文いらないっていうのはそうです。追記しておきます。
pig_vba

2023/01/26 06:46

>そもそも繰り返しの関数を使う事自体間違いだったのでしょうか、、 全然間違いではないです。私が単純にループ処理を使う必要がないと感じただけなので
Zuishin

2023/01/26 08:07 編集

ループを使う必要はありません。たとえば攻撃力が 3 で回復力が 2 HP が 4 の場合、2 ターンで勝負がつきますが、掲載されたコードでは誤答します。 一撃で勝負がつく場合というのは、0 回の攻撃の後に一撃なので、n 回の攻撃の後に一撃というケースと場合分けする必要がないのです。
jimbe

2023/01/26 08:20

空気読まない感じですが >回復以上のダメージで削りきるか "以上" では無く "より大きい" ではないでしょうか。
pig_vba

2023/01/26 23:56

修正しました。しょしんしゃなのでそういうこともよくあります。
fana

2023/01/27 02:41

> 誤答だったので消しますね。 質問者のレベルを推測するに(←失礼な物言いか?), (1) 間違った状態 (2) (1)はどこがどう間違っているのか? という話 (3) (2)を鑑みた修正結果 みたいな話が揃った形で示されるならば有益なのではないかと思うので,ちょっともったいないかな,という気がしました. (が,そんなのはそもそも teratail でやることではないのかもしれません)
pig_vba

2023/01/27 05:15 編集

>ちょっともったいないかな 解答部分を削除した理由についてですが、そもそも間違っている箇所が計算式で、正しい数式が既に他の方によって解答されている以上、後から丸パクリするのはそれこそ回答者に対して不誠実だと判断したからです。 正しい仕様として評価されるべきはたまたま回答が早かっただけの私ではないので戻すつもりはないです
guest

0

int h1 = h - a;
int h2 = h2 + b;
とあるが、この時点でh、a、bがまだ不定。
当然h1、h2も不定となる。
よってこの宣言の仕方はNG。

h2の値が不定なので、その次のfor文もh2までとしているが何回ループするか分からない。(回らないかもしれない)
よって、scanfで確定させる方が先。
そもそもここのscanfのループは何をしたいのでしょうか。
次のif文も同様にh2が不定なので正常に判定できない。

h、a、bはいつ決まるのがいいのか考えてみましょう。
何度も繰り返すにしても最初決まっていてほしい値があるでしょう。
倒しきりたいならば、どこまでをループ内に納めるのがいいでしょうか。
さて上記を頭に入れて、
紙でも何でもいいのでやりたいことを順番に箇条書きしてみよう。(フローチャートが望ましいけれども)
必要な部品はだいたい書けているので、あとはその通り組み立てるだけです。

投稿2023/01/26 06:00

ardin

総合スコア544

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

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

tssubaru

2023/01/26 08:35

h a bは一番最初に決めておくべき値。 ここを決めておくことで、ループする範囲を決められるということでしょうか。
ardin

2023/01/26 11:47 編集

>ここを決めておくことで、ループする範囲を決められるということでしょうか。 広義的にはそうなりますね。 大元の質問の回答としては他の方がいくつか示しているのでほぼ割愛するとして、 折角の勉強でもあるわけですし、いろいろ試してみるのもいいでしょう。 h=敵のHP a=1ターンで与えるダメージ b=1ターンで回復する値 倒せる場合、敵のHPを全て削りたいのでループ回数はhの値を常に比較してみればいいわけですし。 雑に書くと for(0~h)  hはダメージと回復で変動 このループを抜けた時には倒せていて、ターン数もわかります。 倒せない場合はこのループを延々と回るだけなので、先に判定が必要です。 倒せない条件はすぐに分かるでしょう。一撃で倒せない&回復量の方が大きい場合ですね。
guest

0

=という紛らわしい記号を導入したC言語にも問題はありますが、
まずあなたは代入が理解できていません。

数学における=は等しいという関係性を表すものでした。
a=b の後だろうが前だろうが、b=2と記述されていれば、a=2です。

C言語の=は代入、すなわちその行を実行した時点の値のコピーです。

C

1a=1; 2b=a; 3a=2;

これを最後まで実行した場合、a2b1です。

それを踏まえると

C

1 int h; 2 int a; 3 int b; 4 int n; 5 int h1 = h-a; 6 int h2 = h1+b;

というのはあなたの意図を反映していないのがわかると思います。
int h1 = h-a;の時点でのh, aは標準入力から得る前の値(不定)です。


あとは問題文を見ないと断言できないんですが、
おそらく入力は

h a b

の1行だけだと思います。

投稿2023/01/26 04:33

ozwk

総合スコア13521

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

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

tssubaru

2023/01/26 05:45

なるほど、、まだまだ勉強不足ですね。 scanf関数で数値を得てから、配置すべきという解釈であっているでしょうか、、
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問