poj1787 Charlie's Change (多重背包+记录路径)

Charlie’s Change
Time Limit: 1000MS Memory Limit: 30000K

Description
Charlie is a driver of Advanced Cargo Movement, Ltd. Charlie drives a lot and so he often buys coffee at coffee vending machines at motorests. Charlie hates change. That is basically the setup of your next task.

Your program will be given numbers and types of coins Charlie has and the coffee price. The coffee vending machines accept coins of values 1, 5, 10, and 25 cents. The program should output which coins Charlie has to use paying the coffee so that he uses as many coins as possible. Because Charlie really does not want any change back he wants to pay the price exactly.

Input
Each line of the input contains five integer numbers separated by a single space describing one situation to solve. The first integer on the line P, 1 <= P <= 10 000, is the coffee price in cents. Next four integers, C1, C2, C3, C4, 0 <= Ci <= 10 000, are the numbers of cents, nickels (5 cents), dimes (10 cents), and quarters (25 cents) in Charlie’s valet. The last line of the input contains five zeros and no output should be generated for it.

Output
For each situation, your program should output one line containing the string “Throw in T1 cents, T2 nickels, T3 dimes, and T4 quarters.”, where T1, T2, T3, T4 are the numbers of coins of appropriate values Charlie should use to pay the coffee while using as many coins as possible. In the case Charlie does not possess enough change to pay the price of the coffee exactly, your program should output “Charlie cannot buy coffee.”.

Sample Input

12 5 3 1 2
16 0 0 0 1
0 0 0 0 0

Sample Output

Throw in 2 cents, 2 nickels, 0 dimes, and 0 quarters.
Charlie cannot buy coffee.

这道题真的让我真的是:活到老,WA到老!
前前后后一共WA十几次把,真的是够了!还是好好写一个总结把。

首先这道题的意思就是,用151025的硬币凑n元,要求使用的硬币最多,并输出每种硬币的使用数量。

完全背包是完全可以写的。
普通的完全背包直接O(v*k*4),直接GG了。
我们考虑使用优化的完全背包,复杂度就可以跑过去了。
然后,我一开始是这样的:
for(int i=1; ifor(int j=n; j>=a[i].w; --j) {
         if(dp[j-a[i].w]+a[i].w>dp[j]) {
              dp[j]=dp[j-a[i].w]+a[i].w;
              path[j][0]=i;
              path[j][1]=j-a[i].w;
         }
     }
 }
直接WA到老了,但是玄学的是我对拍1e4组数据都是Accpeted的。很让我绝望,估计这道题poj的数据是真的强!1e4组数据都对拍过了还是没有放过我。
其实这个状态方程肯定是错的,题目求的是最多的硬币,而我的dp[j]表示在j价格下价值的最大。压根和硬币没关系!我自己都不知道怎么会对1e4组数据..
然后我上网找答案,发现别人都是这么写的
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int i=1; ifor(int j=n; j>=a[i].w; --j) {
        if(dp[j-a[i].w]>0&&dp[j-a[i].w]+a[i].num>dp[j]) {
            dp[j]=dp[j-a[i].w]+a[i].num;
            path[j][0]=i;
            path[j][1]=j-a[i].w;
        }
    }
}
这个dp[0]等于1真的有点玄学,还有每一次状态转移必须要>0才可以,原因是因为这里的硬币状态转移的前提是前面这个dp也是成立的。前面的这个价值你必须能用某些硬币凑出来,然后我用剩下的硬币凑上去才能对!
#include
#include
#include
#include
#include
using namespace std;

const int maxn=100;
const int maxm=10005;
const int inf=0x3f3f3f3f;
int num[4];
int ans[26];
int price[4]= {1,5,10,25};
int dp[maxm];
int path[maxm][2];
int n;

struct note {
    int num,pri,w;
} a[maxm];

int main() {
    while(scanf("%d%d%d%d%d",&n,&num[0],&num[1],&num[2],&num[3])==5&&(n+num[0]+num[1]+num[2]+num[3])) {
        memset(dp,0,sizeof(dp));
        int top=1;
        for(int i=0; i<4; ++i) {
            int t=1,sum=num[i];
            while(t1;
            }
            if(sum) {
                a[top].num=sum;
                a[top].w=sum*price[i];
                a[top++].pri=price[i];
            }
        }
        memset(path,0,sizeof(path));
        dp[0]=1;
        for(int i=1; ifor(int j=n; j>=a[i].w; --j) {
                if(dp[j-a[i].w]>0&&dp[j-a[i].w]+a[i].num>dp[j]) {
                    dp[j]=dp[j-a[i].w]+a[i].num;
                    path[j][0]=i;
                    path[j][1]=j-a[i].w;
                }
            }
        }
        if(!dp[n]) {
            printf("Charlie cannot buy coffee.\n");
        } else {
            memset(ans,0,sizeof(ans));
            int now=n;
            while(now) {
                int f=path[now][0];
                ans[a[f].pri]+=a[f].num;
                now=path[now][1];
            }
            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",ans[1],ans[5],ans[10],ans[25]);
        }
    }
    return 0;
}

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