匯東網


C 程序設計 - 貨幣兌換

[編輯] [转简体]
|
作者:huidong | 分類:【編程】NOJ 和 C 程序設計習題
[ 18 瀏覽 0 評論 4 贊 4 踩 ]

概要
你現在有 m 元,需要把它兌換成現金,現金的面值由你任給的(比如 1, 3, 5, 9),那麼怎樣兌換才能使得現金的張數最少?(貪心算法可能不是最優解)

正文

第一個版本(遞歸窮舉)

#include <stdio.h>
#include <conio.h>
#include <string.h>

// 更新最佳方案的副本
// [out] _pCopy        副本
// [in] _pBest        最佳兌換方案
// [in] _nBest        最佳兌換方案所用鈔票數
void UpdateBestSchemeCopy(int*& _pCopy, int* _pBest, int _nUsed)
{
    if (_pCopy)
    {
        delete _pCopy;
    }

    _pCopy = new int[_nUsed];
    memcpy(_pCopy, _pBest, _nUsed * sizeof(int));;
}

// 打印數組
void PrintArray(int* _pArray, int num)
{
    printf("需要 %d 張鈔票:", num);
    for (int i = 0; i < num; i++)
    {
        printf("%d ", _pArray[i]);
    }
    printf("\n");
}

// 選擇兌現後張數最少的鈔票面值
// 
// [in] _nTotal        總金額
// [out] _pnBest    數組,存放最佳方案(用戶調用時傳入一個空指針即可)
// [in] _nIndexMin    內部使用,指定最小面值下標,防止重複的面值組合
// 
// 返回最佳方案所用的鈔票數
int ChooseParValue(int _nTotal, int*& _pnBest, int _nIndexMin = 0)
{
    // 記錄遞歸頭
    bool isHead = true;
    static bool isBody = false;
    if (!isBody)    isBody = true;    // 使位於後面的遞歸意識到自己不是頭部了
    else            isHead = false;

    // 所有可用面值(必須從大到小排列,且必須有 1 存在,暫時不處理沒有 1 的情況)
    static int pnParValues[] = { 103, 51, 13, 12, 9, 2, 1 };

    // 記錄當前方案
    static int nUsed = 0;
    static int* pnValuesUsed = nullptr;
    
    // 記錄最佳方案使用的鈔票張數
    static int nBestUsed = 0;

    // 在遞歸頭進行初始化
    if (isHead)
    {
        nUsed = 0;
        pnValuesUsed = new int[_nTotal];
        memset(pnValuesUsed, 0, _nTotal * sizeof(int));

        nBestUsed = _nTotal + 1;
    }

    // 由於在遞歸中,nUsed 會被深層遞歸修改,因此此處需要記錄到這一步已經用了幾張鈔票
    // 以便重置到當前狀態,換下一張鈔票重試
    int nHaveUsed_This = nUsed;    
    
    // 遍歷所有面值
    for (int i = _nIndexMin; i < sizeof(pnParValues) / sizeof(pnParValues[0]); i++)
    {
        // 跳過大於總金額的面值
        if (_nTotal < pnParValues[i])
        {
            continue;
        }

        pnValuesUsed[nHaveUsed_This] = pnParValues[i];
        nUsed = nHaveUsed_This + 1;                // 重置 nUsed

        // 已經兌換完成
        if (_nTotal == pnParValues[i])
        {
            // 輸出方案
            PrintArray(pnValuesUsed, nUsed);
            //_getch();

            // 判斷是不是更佳方案
            if (nUsed < nBestUsed)
            {
                UpdateBestSchemeCopy(_pnBest, pnValuesUsed, nUsed);
                nBestUsed = nUsed;
            }
        }
        // 繼續兌換
        else
        {
            // 此處傳入 i 作爲最小貨幣面值下標,是爲了防止重複的貨幣組合出現。
            // 如果一個組合 12 2 9 出現,那麼必然在它之前已經給出過 12 9 2 的組合,
            // 即爲了確保組合的唯一性,只需要確保 a1 >= a2 >= a3 以此類推。
            ChooseParValue(_nTotal - pnParValues[i], _pnBest, i);
        }
    }

    // 重置遞歸頭
    if (isHead)
        isBody = false;

    return nBestUsed;
}

int main()
{
    // 輸入需要兌換的金額總數,只接受整數
    unsigned int n = 0;
    scanf_s("%u", &n);        // 示例數據 24,貪心算法不是最優解

    int* pBestScheme = nullptr;
    int nBestNum = ChooseParValue(n, pBestScheme);

    printf("\n【最佳方案】\n");
    PrintArray(pBestScheme, nBestNum);

    return 0;
}


[ 4] [ 4]


 評論區  0 條評論

+ 添加評論