题目链接:http://poj.org/problem?id=1787
题目大意:给定一个数p,要求用四种币值为1,5,10,25的硬币拼成p,并且硬币数要最多,如果无解输出"Charlie cannot buy coffee.",1<=p<=1万,1<=硬币数量<=1万
解题思路:看到题目的第一想法是多重背包,把币值和p看成容量,硬币数看成价值,问题就转换为最容量为p的背包中价值最大为多少?刚入手用二进制处理将多重背包转换为01背包然后求解,速度奇慢,跑了969ms,优化后变成675ms。复杂度度为O(p*sum(log(num[i]))。AC之后看别人的解题报告,惊讶地发现他们都用完全背包写这题,复杂仅为(4*p),速度快了5倍多。
两种背包的状态转移方程都一样:dp[j] = max(dp[j],dp[j-mon[i]+val[i])(多重背包解法val[i]>=1,1<=i<=tot,完全背包解法val[i]=1,1<=i<=4)
测试数据:
12 5 3 1 2
16 16 0 0 1
1 0 0 0 0
代码:
//完全背包
#include <stdio.h>
#include <string.h>
#define MAX 20000
int p,dp[MAX],path[MAX];
int num[50],mon[50],used[MAX];
int main()
{
int i,j,k,tp;
mon[1] = 1,mon[2] = 5;
mon[3] = 10,mon[4] = 25;
while (scanf("%d",&p)) {
for (i = 1; i <= 4; ++i)
scanf("%d",&num[i]);
if (p == 0) break;
memset(dp,0,sizeof(dp));
memset(path,0,sizeof(path));
path[0] = -1,dp[0] = 1;
for (i = 1; i <= 4; ++i) {
memset(used,0,sizeof(used));
for (j = mon[i]; j <= p; ++j)
if (dp[j-mon[i]] + 1 > dp[j] && dp[j-mon[i]]
&& used[j-mon[i]] + 1 <= num[i]) {
dp[j] = dp[j-mon[i]] + 1;
used[j] = used[j-mon[i]] + 1;
path[j] = j - mon[i];
}
}
if (dp[p] != 0) {
memset(num,0,sizeof(num));
while (path[p] != -1) {
num[p-path[p]]++;
p = path[p];
}
printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",num[1],num[5],num[10],num[25]);
}
else printf("Charlie cannot buy coffee.\n");
}
return 0;
}
//多重背包
#include <stdio.h>
#include <string.h>
#define MAX 20000
#define INF 1000000000
#define max(a,b) (a)>(b)?(a):(b)
int num[5],mon[5],in[MAX];
int tot,cost[MAX],val[MAX];
int p,ans,dp[100][MAX],path[100][MAX];
void Solve_1A() {
int i,j,k;
tot = 0;
for (i = 1; i <= 4; ++i) {
for (j = 0; (1<<j) <= num[i]; ++j) {
k = 1<<j;
num[i] -= k;
cost[++tot] = k * mon[i];
val[tot] = k;
in[tot] = i;
}
if (num[i]) {
in[++tot] = i;
val[tot] = num[i];
cost[tot] = num[i] * mon[i];
}
num[i] = 0; //最后拿来存放答案,老师说要懂得回收利用
}
for (i = 0; i <= tot; ++i)
for (j = 0; j <= p; ++j)
path[i][j] = 0,dp[i][j] = -INF;
dp[0][0] = 0;
for (i = 1; i <= tot; ++i) {
for (j = p; j >= 0; --j)
if (j >= cost[i]) {
if (dp[i-1][j-cost[i]] != -INF
&& dp[i-1][j-cost[i]] + val[i] > dp[i-1][j])
dp[i][j] = dp[i-1][j-cost[i]] + val[i],path[i][j] = 1;
else dp[i][j] = dp[i-1][j];
}
else dp[i][j] = dp[i-1][j];
}
if (dp[tot][p] != -INF) {
j = tot,k = p;
while (j > 0) {
if (path[j][k])
num[in[j]] += val[j],k -= cost[j];
j--;
}
}
}
int main()
{
int i,j,k;
mon[1] = 1,mon[2] = 5;
mon[3] = 10,mon[4] = 25;
while (scanf("%d",&p)) {
for (i = 1; i <= 4; ++i)
scanf("%d",&num[i]);
if (p == 0) break;
Solve_1A();
if (dp[tot][p] == -INF)
printf("Charlie cannot buy coffee.\n");
else
printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n",num[1],num[2],num[3],num[4]);
}
return 0;
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。