#author("2019-12-23T18:56:03+09:00","default:tutimura","tutimura") ** 問題1:条件を満たすものの数を数える [#v2fc4c6b] 底辺 b, 高さ h の三角形を考える。ただし b, h ともに整数で、10≦b≦20, 10≦h≦20 の範囲をとるものとする。この中で、面積が 70 以上 97 以下になるものの数を数えたい。次の指示に従って、プログラムを完成させなさい。 + 底辺 b, 高さ h の三角形の面積を返す関数 double triangle(int b, int h) を作りなさい。 + main() 関数で、底辺5, 高さ5 の三角形の面積を、関数 triangle() を用いて計算し、printf() で表示しなさい。正当な値が表示されることも合わせて確認しなさい。 + main() 関数を作り直して、底辺 10〜20, 高さ 10〜20 の範囲の三角形の面積をすべて表示するようにしなさい。for ループが2重の入れ子になることを期待している。 + (内側の)ループの中に if 文を追加して、三角形の面積が 70〜97 の場合のみ、表示するように変更しなさい。 + 数を数えるための変数 int count を main() 関数の中で宣言し、これを利用して三角形の面積が 70〜97 となるものの数を数えなさい。 + main() 関数の最後で「条件を満たす三角形は x 個あります。」と表示しなさい。 + ループ内の printf() をコメントにしなさい。消さずにコメントで残しておくのがよい。 - ねらい -- 複雑な動作をするプログラムを、一気に完成させるのは難しい。 -- 効率よくプログラムを作るには、小さなパーツに分解して、計算結果を表示させるなどして、少しずつ動作を確認しながら完成させるとよい。 -- しかし、どのように問題を分解して、小さなパーツに分ければよいのか、一口に説明するのは難しい。上記のような誘導がひとつの参考になる。 -- 三角形の面積計算を関数に独立させる有難みを感じてほしい。もし関数の処理内容が複雑であったらどうなるかを考えるとわかるはず。 ---- ** 問題2:硬貨での支払い [#w1a8f329] プログラムは、まず1.について作り、続けて改造して2.を解いて、2.のみを提出しなさい。 今、手元に次の硬貨がある。 10円 2枚 50円 5枚 100円 1枚 500円 2枚 + これらの硬貨を使って、自動販売機の720円の商品を買いたい。支払い方は何通りかあるが、用いる硬貨の枚数がもっとも少ない場合に何枚になるかを調べなさい。なお、自動販売機はつり銭切れになっているので、ちょうどの金額を支払いたい。 > ヒント1:硬貨の枚数から合計金額を求める手続きを、関数に独立させてみよ。 > ヒント2:最小値を求める手順を復習せよ。「暫定の最小値を更新する」手順である。 < + 購入しようとすると、自動販売機が不調で、最大で5枚の硬貨しか受け付けないことがわかった。そこで、5枚以下の硬貨を用いて、720円以上のもっとも安い金額を支払うことにする。いくら支払えばよいか調べなさい。 > 提出するのは 2. のプログラムだけでよい。 ---- * 価格の割引 [#k85da117] ある市販のソフトウェアAとソフトウェアBがある。 このソフトウェアの価格には、購入本数に応じた値引きがあり、 1本あたりの単価が購入本数に応じて決まる。 このため、価格設定によっては「購入本数の多いほうが、購入総額が安くなる」という 逆転現象が起こることがある。 この逆転現象について調査するプログラムを、以下の指示に従って作りなさい。 ** 問題3:ソフトウェアA [#t189ce70] まず、ソフトウェアAについて考える。 単価は以下のようになっている。 |~本数|~単価|h |~4本|5000円| |5~9本|4500円| |10本~|3500円| + 購入本数 n に応じた単価を計算する関数 int priceA(int n) を作りなさい。 > ヒント:条件分岐に無駄がないように注意せよ。 < + 購入本数 n に応じた購入総額を計算する関数 int total_priceA(int n) を作りなさい。n は 0 以上であることを前提としてよい。 > ヒント:この関数に条件分岐が必要か、よく検討せよ。 < + main() 関数で、購入本数を20から1まで変化させ、それぞれの本数について、次の項目を表示しなさい。 -- 購入本数 -- 単価 -- 総額 -- これまで表示した総額の中での最小値 > ヒント:購入本数が減るように''逆順''のループを回すことに注意せよ。 総額の最小値は、ほとんどの場合に更新される。 < + 3. で作った main() 関数を改造して、逆転現象の起こっている部分(もっと多い本数を購入すると総額が安くなるケース)のみを表示するようにしなさい。 > ヒント:総額の最小値を更新しなかった場合に限って表示すればよい。 ---- ** 問題4:ソフトウェアB [#f62d0477] 次に、ソフトウェアBについて考える。 単価は以下のようになっている。 |~本数|~単価|h |~4本|5000円| |5~9本|4800円| |10~14本|4600円| |15~19本|4500円| |20~49本|4300円| |50~79本|4100円| |80~99本|3600円| |100~999本|3500円| |1000本~|3300円| このように価格表が大きく複雑であるので、 価格の改定があった場合にプログラムの修正作業が楽になるように工夫したい。 つまり、ソフトウェアAの場合とは違う構造のプログラムを作りたい。 + ソフトウェアAの場合と同様に、購入本数 n に応じた単価を計算する関数 int priceB(int n) を作りなさい。ただし、関数での処理(条件文)に価格を直接書き込むのではなく、 配列に価格を書き並べるようにしなさい。 例えば、次のような2つの配列を大域変数(グローバル変数)に宣言するとよい。 (これ以外の配列の使い方をしてもよい。) int pricesB[] = { 5000, 4800, 4600, 4500, 4300, 4100, 3600, 3500, 3300 }; int numsB[] = { 5, 10, 15, 20, 50, 80, 100, 1000, -1 /* 番兵 */ }; > ヒント1:numsB[] 配列を先頭要素から順番にアクセスするループは for (i=0; numsB[i]>=0; i++) { ... } のように書ける。 参考までに、配列の最後を表す特別な値(-1)の要素は、「番兵」と呼ばれる。 > ヒント2:配列には複数の要素が含まれることから、配列をあらわす変数名に、英単語の複数形を用いる習慣がある。 またこの例では、関数名と区別する目印の役目も果たしている。 < + ソフトウェアAの場合と同様に、購入本数 n に応じた購入総額を計算する関数 int total_priceB(int n) を作りなさい。main() で関数の値を表示して、動作を確かめなさい。 > ヒント:total_priceA() で条件分岐をした人は、ここでもう一度考えなおして欲しい。条件分岐をせずにすませた人は、その利点を改めて感じ取って欲しい。 < + 購入者がソフトウェアBを注文してきた時に、逆転現象の起こる本数であれば 「何本購入すればもっと安くなります」と提案したい。 そこで、必要とされる本数 n 以上で、もっとも総額が安価になる本数を返す関数 int suggestB(int n) を作りなさい。 さらに main() で関数の値を表示して、この関数の動作を確かめなさい。つまり、逆転現象の起こる本数をすべて表示しなさい。 > ヒント1:関数の動作としては、逆転現象の起こらない場合は、 与えられた n をそのまま返すことになる。 > ヒント2:簡単のため、1000本よりも多く購入しても、それ以上は単価の値引きが起こらないことを利用してもよい。 > ヒント3:関数を1000という数値に依存しないで記述できると、なおよい。ちなみに、-1 という数値は番兵をあらわすので、依存してよい。 ---- ** 問題5:発展問題 [#m27e492f] 問題3と問題4の処理を共通化したい。値段表をあらわす配列を引数として受け取る関数 int price(int n, int prices[], int nums[]) int total_price(int n, int prices[], int nums[]) int suggest(int n, int prices[], int nums[]) を作り、2種類の配列を切り替えることで、これまでに作った関数 priceA(), total_priceA(), priceB(), total_priceB(), suggestB() と同じ動作をさせなさい。 ソフトウェアAとBそれぞれに配列を準備して、 main() 関数から作成した関数に引数として与えて呼び出して、動作を確かめなさい。 > ヒント:1000のような数値には依存してはいけない。 ---- ** 問題1の答え合わせ [#s80bc40e] + (マウスで選択すると見える)→ &color(white,white){34}; 個← ~ 36個になった場合は間違いで、→ &color(white,white){小数つきの計算ができているのか}; に注意せよ。← ** 問題2の答え合わせ [#s80bc40e] + 6枚 + (マウスで選択すると見える)→ &color(white,white){750}; 円← ** 問題3の答え合わせ [#qac1ae64] ソフトウェアAで逆転現象の起こる本数は、9本と8本。 ** 問題4の答え合わせ [#s80bc40e] ソフトウェアBで逆転現象の起こる本数は(マウスで選択すると見える)→ &color(white,white){ n=999〜943, 99〜98, 79〜71, 49〜48}; ←