<?php
/*
* https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1420112999
*
* ceburreさん2008/10/2322:12:32
* 大学のプログラミング課題ができません。
* 最近大学でjavaを始めたんですが課題に行き詰まってます、課題文は以下のとおりです。
*
* 「たとえば現在の所持金額が1443円であるとき、667円の買い物をしたとする。
* こういう場合にわれわれは、財布の中に小銭が増えるのを嫌って千円札1枚で支払うのではなく、わざと1222円を支払うということをする(するとおつりが555円になる)。
* このように、現在の所持金額と購入金額とを入力すると、最適の支払い方法を教えるプログラミングをつくれ。
* 簡単のため、千円札を最高額紙幣とする。 また10円玉を14枚持っているような異常な状況は考慮しない。」
*
* という課題です。正直初心者にここまでやらせるのはどうかと思ってしまうマス。
* 一応、この講義ではキーボードでの入力と条件の判断(ifとかelseとか)を教わりました。それを使って出来るはづだと思うんですが・・・。
*
* 気が向いたらどうか回答よろしくおねがいしますm(_ _)m *
*/
// 所持金
$myAmount = 1443;
// 購入金額
$purchaseAmount = 667;
// 出力
echo "現在の所持金 : " . $myAmount . "円" . PHP_EOL;
echo "購入金額 : " . $purchaseAmount . "円" . PHP_EOL;
// 最適な支払金額を計算
$payingAmount = calcPayingAmount ( $myAmount, $purchaseAmount );
// 結果出力
if ($payingAmount < 0) {
// 所持金が足りなかった
$shortageAmount = - $payingAmount;
echo "所持金が{$shortageAmount}円不足しています";
} else {
echo "支払金額 : " . $payingAmount . "円" . PHP_EOL;
}
// 関数
function calcPayingAmount($myAmount, $purchaseAmount) {
// 財布と相談
if ($myAmount < $purchaseAmount) {
// 買えない
return $myAmount - $purchaseAmount;
}
// 所持金インスタンス生成
$objMyAmount = new Amount ( $myAmount );
// 支払を実行し、支払った金額配列を取得
// 知りたければどの貨幣を何枚使ったかの内訳もわかるということ
$aryPayingAmount = $objMyAmount->pay ( $purchaseAmount );
// 支払った金額を整数で返却
$intPayingAmount = Amount::arrayToInt ( $aryPayingAmount );
return $intPayingAmount;
}
// クラス
class Amount {
private $ary = [
10000 => 0,
5000 => 0,
1000 => 0,
500 => 0,
100 => 0,
50 => 0,
10 => 0,
5 => 0,
1 => 0
];
public function getArray() {
return $this->ary;
}
public function __construct($intAmount) {
$this->ary = self::intAmountToAry ( $intAmount );
}
public function __toString() {
$intAmount = $this->getInt ();
return "" . $intAmount;
}
public function getInt() {
$intAmount = 0;
foreach ( $this->ary as $key => $value ) {
$intAmount += $key * $value;
}
return $intAmount;
}
public function dispArray() {
echo "<pre>";
print_r ( $this->ary );
echo "</pre>";
}
public static function intAmountToAry($intAmount) {
$ary = [
10000 => 0,
5000 => 0,
1000 => 0,
500 => 0,
100 => 0,
50 => 0,
10 => 0,
5 => 0,
1 => 0
];
foreach ( $ary as $key => &$value ) {
if (intval ( $intAmount / $key )) {
$value += intval ( $intAmount / $key );
$intAmount = intval ( $intAmount % $key );
}
}
return $ary;
}
public static function keyReverse($ary) {
foreach ( $ary as $key => $value ) {
$keys [] = $key;
}
for($i = count ( $keys ) - 1; 0 < $i; $i --) {
$key = $keys [$i];
$reversedAry [$key] = $ary [$key];
}
return $reversedAry;
}
// 支払い関数。支払えなければfalse返却
public function pay($intPurchaseAmount) {
if ($this->getInt () < $intPurchaseAmount) {
// 支払えない
return false;
}
// 支払える
// 支払い前の配列をバックアップ
$aryBeforePaying = $this->ary;
// 支払金額を配列にする
$aryPurchaseAmountDesc = self::intAmountToAry ( $intPurchaseAmount );
// 支払金額の配列をキーの昇順にする
$aryPurchaseAmountAsc = self::keyReverse ( $aryPurchaseAmountDesc );
// キーが1円から始まるようにした支払金額の配列をforeach
foreach ( $aryPurchaseAmountAsc as $key => $value ) {
// 小銭から比較するよ
if ($value < $this->ary [$key]) {
// ジャストで払える
$this->ary [$key] -= $value;
} else {
// 足りない
// 大きい金額から支払う
foreach ( $aryPurchaseAmountAsc as $myKey => $dummy ) {
if (($key * $value < $myKey) && (0 < $this->ary [$myKey])) {
$intPurchaseAmount -= $myKey;
$this->ary [$myKey] -= 1;
break;
}
}
}
// 支払終了判定
if ($intPurchaseAmount <= 0) {
break;
}
}
// 支払った金額配列取得
$payingAmountArray = self::getPayingAmountArray ( $aryBeforePaying, $this->ary );
// 支払った金額配列返却
return $payingAmountArray;
}
public static function getPayingAmountArray($before, $after) {
foreach ( $before as $key => $value ) {
$payingAmountArray [$key] = $before [$key] - $after [$key];
}
return $payingAmountArray;
}
// 数値が5000,500,50,5のいずれかならtrueを返却
public static function isFamilyOfFive($n) {
while ( 10 <= $n ) {
$n = intval ( $n / 10 );
}
if (5 === $n) {
return true;
} else {
return false;
}
}
public static function arrayToInt($ary) {
$total = 0;
foreach ( $ary as $key => $value ) {
$total += $key * $value;
}
return $total;
}
}