1. 程式人生 > >清華機試-最小郵票數

清華機試-最小郵票數

不用 mes 最小 spa AC div 時間 lse n)

題目描述

有若幹張郵票,要求從中選取最少的郵票張數湊成一個給定的總值。 如,有1分,3分,3分,3分,4分五張郵票,要求湊成10分,則使用3張郵票:3分、3分、4分即可。

輸入描述:

有多組數據,對於每組數據,首先是要求湊成的郵票總值M,M<100。然後是一個數N,N〈 20,表示有N張郵票。接下來是N個正整數,分別表示這N張郵票的面值,且以升序排列。

輸出描述:

對於每組數據,能夠湊成總值M的最少郵票張數。若無解,輸出0。

示例1

輸入

10

5

1 3 3 3 4

輸出

3

解題思路

一開始看到這個題目,想到的是bfs。bfs的思路是:枚舉所有的可能性,最多有2的19次方種情況(524288種)。可以從0列到524287,一個數的二進制形式表示選或者不選這張郵票。由於對於每一個數字最多要列舉19位,所以時間復雜度大約是O(19*2^19)。比較高但是應該也是可以通過的,因為其實有一些數字不用列舉那麽多位。

後來想到可以用動態規劃來解決這個問題。如果將狀態dp[i][j]定義為前i張郵票湊成總值j所需要的最少郵票數。則狀態可以初始化為dp[i][0]=0。(其中,1<=i<N,0<=j<M)。

狀態轉移方程如下:

dp[i][j] = min(dp[i-1][j],dp[i-1][j-v[i]])。(其中,i>0,j-v[i]>=0)

同時註意判斷dp[i][j]是否等於-1。-1表示dp[i][j]無解。

代碼

#include <iostream>

#include <cmath>

using namespace std;

int dp[20][100] = {-1};

int num[20];

int main()

{

    cout << pow(2,19) * 19 << endl;

    int m,n;

    while(cin >> m >> n)

    {

        int ans = 20;

        for(int i = 0;i < n;i++)

        {

            dp[i][0] = 0;

            for(int j = 1;j <= m;j++)

                dp[i][j] = -1;

        }

        for(int i = 0;i < n;i++)

            cin >> num[i];

        for(int i = 0;i < n;i++)

        {

            for(int j = num[i];j <= m;j++)

            {

                if(i == 0)

                {

                    if(j == num[i]) dp[i][j] = 1;

                    else dp[i][j] = -1;

                }

                else

                {

                    int a = 20;

                    if(dp[i - 1][j - num[i]] != -1)

                        a = dp[i - 1][j - num[i]] + 1;

                    int b = 20;

                    if(dp[i - 1][j] != -1)

                        b = dp[i - 1][j];

                    int c = min(a,b);

                    if(c == 20) dp[i][j] = -1;

                    else dp[i][j] = c;

                }

            }

        }

        if(dp[n - 1][m] == -1) cout << 0 << endl;

        else cout << dp[n - 1][m] << endl;

    }

    return 0;

}

  

清華機試-最小郵票數