wanimaru47's diary

プログラミング等々

解析的な解法

新年明けましておめでとうございます。

 今日は新年早々AOJで問題を解きました。
 その問題の解法について解説を読んでみる勉強になったことがあったので、メモしておこうと思います。
 まず、この問題(ここから)は、解説によると全検索でも求められるそうですが、4つの硬貨を用いる問題なのでループの数が面倒になります。そこで、よく自販機なので用いる両替テクニックを実際に考えてみることにしてみます。まず、考えることはちょうど硬貨が支払えるかと言うことを考えるはずです。この時に財布で起こってることは各硬貨について枚数が足りているかを考えます。枚数が足りないときは、代金より多いお金を支払い、おつりを貰います。また、100円のものを買うとき財布に10円玉が20枚あるとします。この時の支払いは10円を20枚支払い、1枚の100円硬貨をおつりとしてもらうことが硬貨の枚数削減の最良の手法になります。

 この考えを式に直します。
  例として次のような場合を考えます。
   
  支払額 => 530
  10円 => 1枚、50 => 13枚、100円 => 7枚、500円 => 16枚

  支払い後の残金は8820円になります。この代金について硬貨の枚数を一番少なくしてみます。
  8820 ÷ 500 = 16・・・320
   320 ÷ 100 = 3 ・・・ 20
    20 ÷  50 = 0 ・・・ 20
    20 ÷  10 = 2 ・・・  0

  この各硬貨の枚数について、元々所持していた枚数と差をとります。
  16 ー 16 =  0
   7 ー  3 =  4
  13 ー  0 = 13
   1 ー  2 = −1

  この解は各貨幣について
        解 > 0 ・・・ 財布から支払った貨幣の枚数
        解 = 0 ・・・ 財布の中の貨幣の枚数が変化していない
        解 < 0 ・・・ おつりとして増えた貨幣の枚数

  つまり、この問題は上の作業で得られた解の0より大きい枚数について回答します。

 この考えをコードにしたものです。

#include <iostream>
using namespace std;

int main()
{
  int P, price[4][2] = {{10,}, {50,}, {100,}, {500,}};
  bool flag = false;
  while (cin >> P && P) {
    int total = 0;
    for (int i = 0; i < 4; i++) {
      cin >> price[i][1];
      total += (price[i][0] * price[i][1]);
    }
    P -= total;
    P = -P;
    for (int i = 3; i >= 0; i--) {
      price[i][1] = price[i][1] - P / price[i][0]; 
      P = P % price[i][0];
    }
    if (flag) cout << endl;
    for (int i = 0; i < 4; i++) {
      if (price[i][1] > 0) cout << price[i][0] << " " << price[i][1] << endl;
    }
    flag = true;
  }

  return 0;
}