您的位置:首页 > 其它

poj 1787 Charlie's Change (多重背包+记录路径)***

2012-08-17 15:16 447 查看
http://poj.org/gotoproblem?pid=1787

(1)本题为一般的多重背包问题,亮点在于记录路径的方法,开了一个数组 pre[10010] 记录前一步的位置。调用时用 sum[30] 逐个累加即可(当然,这里的调用方法还有其他的各种方法)。

   记录:

pre[j]=j-b[i];


   调用:

for(i=m;pre[i]!=-1;i=pre[i])
ans[i-pre[i]]++;


(2)多重背包一如既往,用数组 used[10010] 限制,转化为01背包(或是完全背包,只是形式有点像而已,不属于而这任何一类)。

(3)初始化问题:

1)将dp[i] 预处理为0。 dp[i] 表示 i 元最多用多少硬币 ,但它还有另一重意思,即 i 元可以用已有的硬币表示,其值为真和假(0和1)。换而言之,dp[10010] 完成了两个数组才能完成的任务。然而,第一种含义下,dp[0]=0, 第二种含义下, dp[0]=1,存在矛盾,真的要用两个数组才能完成么?

memset(dp, 0, sizeof(dp));
dp[0]=1;


令 dp[0]=1 时有原因的,因为 dp[0] 的第一种含义并不重要,第二种含义与题目息息相关。

     2)只需令 pre[0]=-1 即可;

pre[0]=-1;


(4)限制条件问题:

if((!dp[j]||dp[j-b[i]]+1>dp[j])&&dp[j-b[i]]&&used[j-b[i]]+1<=a[i])


如果 dp[j] 未求或是太小,dp[j-b[i]] 已求,并且硬币还够用,则进入循环。

具体代码:

View Code

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n, m;
int a[5], b[5]={0,1,5,10,25};
int dp[10010], used[10010];
int pre[10010];
int ans[30];
int main()
{
int i, j, k;
while(scanf("%d", &m)!=EOF)
{
for(i=1;i<=4;i++) scanf("%d", &a[i]);
if(!m&&!a[1]&&!a[2]&&!a[3]&&!a[4]) break;
memset(dp, 0, sizeof(dp));
memset(pre, 0, sizeof(pre));
dp[0]=1; // 表示 dp[0] 已求过
pre[0]=-1;
for(i=1;i<=4;i++)
{
for(j=0;j<=m;j++)
used[j]=0;
for(j=b[i];j<=m;j++)
{
if((!dp[j]||dp[j-b[i]]+1>dp[j])&&dp[j-b[i]]&&used[j-b[i]]+1<=a[i]) //各种限制条件
{
dp[j]=dp[j-b[i]]+1;
used[j]=used[j-b[i]]+1;
pre[j]=j-b[i]; //记录路径,用数组保存前一步的位置
}
}
}
memset(ans, 0, sizeof(ans));
if(dp[m])
{
for(i=m;pre[i]!=-1;i=pre[i]) ans[i-pre[i]]++;
printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", ans[1], ans[5], ans[10], ans[25]);
}
else printf("Charlie cannot buy coffee.\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: