天天看點

hdu2844“Coins“多重背包

hdu2844"Coins"多重背包

一些感受(廢話)

看了一下,可以将多重背包轉化為0/1背包,将某種多個的硬币,對每一個硬币,每一個dp狀态都有選擇/不選擇的0/1方案,由于硬币數可能很多,網上有部落客使用二進制優化方案。這種方案是我第一次接觸。以後需要學習一下。

連結:二進制優化,轉化為0/1背包解決的方案

我的思路

一開始,我想要定義一個數組st[n][m],表示dp[m]狀态硬币A[n]對應的數量,狀态進行轉移時,前狀态必須有對應硬币剩餘才可以進行轉移(狀态轉移的硬币數量限制),但因為1<=n<=100, 1<=m<=100000,送出的時候運作記憶體超限。

但後來發現!!!在狀态轉移算法中,周遊每種硬币進行轉移的時候每次隻需要操作一種硬币,是以隻需要在狀态轉移算法中定義一個一維臨時數組temp[m]存儲對應狀态的硬币數量。記憶體一下子減少了很多。

#include<iostream>
using namespace std;
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
const int maxn = 101;
const int maxm = 1e5+1;//100001
bool dp[maxm]; //dp[m]==true:表示金額為m可以用硬币組合出來
int A[maxn]; //A[i]:對應硬币的面值
int C[maxn]; //C[i]:對應硬币的數量

int m, n;

void init()
{
    memset(dp, false, sizeof(dp));
    dp[0] = true;
}

int solve()//狀态轉移算法:遞推dp,傳回結果
{
    int temp[maxm];//temp[c]:dp[c]狀态對應某一硬币的剩餘硬币數,臨時變量
    for(int i = 1; i <= n; i++){//硬币次元
        for(int c = 0; c <= m; c++){//初始化所有背包狀态的硬币數量
            temp[c] = C[i];
        }
        for(int c = A[i]; c <= m; c++){
            if(!dp[c] && dp[c-A[i]] && temp[c-A[i]] > 0){//目前狀态沒有出現過可行組合&&對應前一個狀态可行且硬币剩餘不為0
                dp[c] = true;//能組合出目前狀态
                temp[c] = temp[c-A[i]] - 1;//硬币數是前一個狀态減去1
            }
        }
    }

    int ans = 0;
    for(int c = 1; c <= m; c++){
        if(dp[c]){
            ans++;
        }
    }
    return ans;
}

int main()
{
    //freopen("in.txt", "r", stdin);
    while(~scanf("%d %d", &n, &m) &&n&&m){//每一個案例
        for(int i = 1; i <= n; i++)//輸入硬币的面值
            scanf("%d", &A[i]);

        for(int i = 1; i <= n; i++)//輸入硬币的數量
            scanf("%d", &C[i]);

        init();//初始化dp數組

        printf("%d\n", solve());
    }

    return 0;
}