poj 1787 Charlie's Change(完全背包 或 多重背包 记录路径)

多重背包,但是可以用完全背包来做。
参考:http://www.cnblogs.com/kuangbin/archive/2012/09/20/2695803.html

#include 
#include 
#include 
using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 10010;
int dp[MAXN];
int path[MAXN];
int used[MAXN];
int P;
int v[4] = {1,5,10,25};
int num[4];
int res[100];

void init()
{
    memset(res,0,sizeof(res));
    memset(path,0,sizeof(path));
    path[0] = -1;
    for(int i = 1; i <= P; ++i)
        dp[i] = -INF;
    dp[0] = 0;
}

void solve()
{
    for(int i = 0; i < 4; ++i)
    {
        memset(used,0,sizeof(used));
        for(int j = v[i]; j <= P; ++j)
        {
            //dp[j-v[i]] >= 0如果不设这个条件。used会溢出
            //而且dp[j-v[i]]<0的话,也没必要去更新,在这之前的状态组合不出来j-v[i]
            if(dp[j-v[i]]+1 > dp[j] && dp[j-v[i]] >= 0 && used[j-v[i]] < num[i])
            {
                dp[j] = dp[j-v[i]]+1;
                used[j] = used[j-v[i]]+1;
                path[j] = j-v[i];
            }
        }
    }
}

int main()
{
    while(scanf("%d",&P))
    {
        scanf("%d %d %d %d",&num[0],&num[1],&num[2],&num[3]);
        if(P+num[0]+num[1]+num[2]+num[3] == 0)
            break;
        init();
        solve();
        if(dp[P] != -INF)
        {
            while(path[P] != -1)
            {
                res[P-path[P]]++;
                P = path[P];
            }
            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",res[1],res[5],res[10],res[25]);
        }
        else
            printf("Charlie cannot buy coffee.\n");
    }
    return 0;
}

多重背包:
参考:http://blog.csdn.net/libin56842/article/details/9470687
学到了记录一种多重背包记录路径的方法。

#include 
#include 
#include 
using namespace std;

const int N = 10010;
int dp[N*10];
int path[N];
int cent[4] = {1,5,10,25};
int num[4];
int res[30];
int P;

void CompletePack(int pos)
{
    for(int j = cent[pos]; j <= P; ++j)
    {
        if(dp[j-cent[pos]]+1 > dp[j])
        {
            dp[j] = dp[j-cent[pos]]+1;
            path[j] = pos*N+j-cent[pos];
        }
    }
}

void ZeroOnePack(int pos, int k)
{
    for(int j = P; j >= cent[pos]*k; --j)
    {
        if(dp[j-k*cent[pos]]+k > dp[j])
        {
            dp[j] = dp[j-k*cent[pos]]+k;
            path[j] = pos*N+j-k*cent[pos];
        }
    }
}

void MultiplePack()
{
    for(int i = 0; i < 4; ++i)
    {
        if(num[i]*cent[i] >= P)
            CompletePack(i);
        else
        {
            int temp = num[i];
            for(int k = 1; k <= temp; k *= 2)
            {
                ZeroOnePack(i,k);
                temp -= k;
            }
            if(temp) ZeroOnePack(i,temp);
        }
    }
}

int main()
{
    int t;
    while(scanf("%d",&P))
    {
        t = P;
        for(int i = 0; i < 4; ++i)
        {
            scanf("%d",&num[i]);
            t += num[i];
        }
        if(!t) break;
        memset(path,0,sizeof(path));
        memset(res,0,sizeof(res));
        for(int i = 1; i <= P; ++i) dp[i] = -N;
        dp[0] = 0;
        MultiplePack();
        if(dp[P] < 0)
        {
            printf("Charlie cannot buy coffee.\n");
            continue;
        }
        while(P)
        {
            int s1 = path[P]/N;//s1表示货币的种类
            int s2 = path[P]%N;//(P-s2)表示这种货币一共多少金额
            res[cent[s1]] += (P-s2)/cent[s1];
            P = s2;
        }
        printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",res[1],res[5],res[10],res[25]);
    }
}

你可能感兴趣的:(背包)