[ACM问题] 一道关于换钱的算法问题

码农首席 2009-03-12 12:03:53
我想问的是那种可能很多人都知道的换钱问题...我是个新手,之前就这个问题在这个论坛上面搜索过,发现有人问过类似的问题,但这些问题的回答往往不够完整....我的能力有限,无法就这些简略的回答弄清楚我的问题,所以发了这个帖,希望各位高手能够给出完整的代码或详细的思想思路解释....

这是四川大学acm上的一道问题,一直无法解决...
原题如下:

New Zealand currency consists of $100, $50, $20, $10, and $5 notes and $2, $1, 50c, 20c, 10c and 5c coins. Write a program that will determine, for any given amount, in how many ways that amount may be made up. Changing the order of listing does not increase the count. Thus 20c may be made up in 4 ways: 1x20c, 2x10c, 10c+2 5c, and 4x5c.


Input
Input will consist of a series of real numbers no greater than $50.00 each on a separate line. Each amount will be valid, that is will be a multiple of 5c. The file will be terminated by a line containing zero (0.00).


Output
Output will consist of a line for each of the amounts in the input, each line consisting of the amount of money (with two decimal places and right justified in a field of width 5), followed by the number of ways in which that amount may be made up, right justified in a field of width 12.


Sample input

0.20
2.00 //这里原题有点错误....
0.00

Sample output

0.20 4
2.00 293


这是原题连接地址:
http://202.115.53.252:8080/soj/problem.action?id=1009

我的答案老是超出时间限制了,但我看那些算法效率排名前几的人,cpu需要时间是0.....
据说是一种动态规划的问题,我虽然在网上看过这种算法的相关思想,但一直没有实践,光是看那些概念,我实在是无法写出
相关代码...T_T......
我感觉我遇到了一个槛,如果我能解决这个问题,说不定我就能跨入学习动态规划算法的大门了.....
诚心请求各位高手指教......

...全文
729 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
baihacker 2009-03-26
  • 打赏
  • 举报
回复
呵,我以前也是乱来.
这里列举了一些水题的.
http://hi.baidu.com/feixue/blog/item/a9e676c686a4f2139d163d38.html
码农首席 2009-03-26
  • 打赏
  • 举报
回复
哇,真厉害,想不到一个星期没登论坛就多了这么多回复。。。。。

感谢各位前辈的指教。。。。。。

我自己经过一个星期的参研,终于也写出了自己的AC代码。。。。。


#include<stdio.h>
#define MAXN 5000
int value[] = {5,10,20,50,100,200,500,1000,2000,5000};

int main()
{
float money;
int m,i,j;
int c[MAXN+1];

for (i = 0;i <= MAXN;i+=5) c[i] = 1;
for (i = 1;i < 10;i++)
{
for (j = value[i];j <= MAXN;j+=5)
c[j] += c[j - value[i]];
}

while(scanf("%f",&money))
{
if (money == 0.00) break;
m = (int)(money * 100);
printf("%5.2f%12d\n", money, c[m]);
}
return 0;
}



不过我还没有参透这里头的思想,可能只做得出这道题而已,换了其他题可能还是做不出来。。。。。

我想问下各位是怎样从基础开始学习算法的,是拿本书自己啃吗?

我发现大学的老师讲编程语言的课时,很少提到一些比较高级的算法,而且也没有说应该怎样自主学习。。。。。

我觉得有时候这种东西没人教,还是要花好久时间去想哦。。。。而且如果找的参考资料不好,还不一定能弄

明白。。。。。。在大学都呆了一个多学期,感觉对编程的学习完全没有一点质的飞跃。。。。。

我现在正在热切期待什么时候能对编程的学习有一种一窍通百窍的感觉。。。。

adfas 2009-03-22
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 shellcoder 的回复:]
水的一题,生产函数的最最基本应用,
[/Quote]

晕,是生成函数
adfas 2009-03-22
  • 打赏
  • 举报
回复
水的一题,生产函数的最最基本应用,
baihacker 2009-03-22
  • 打赏
  • 举报
回复

//顺便贴个AC的代码
//呵自己多学习,可以先做1865这个题
#include <stdio.h>
#include <math.h>
#define MAX 10
long it[1001][MAX+1];
int ss[MAX+1] = {0,1,2,4,10,20,40,100,200,400,1000};

void Init()
{
int i;
for (i = 1; i <= MAX; ++i)
it[1][i] = 1;
for (i = 2; i <= 1000; ++i)
{
int j;
for (j = 1; i > ss[j]; ++j)
it[i][j] = it[i][j-1]+ it[i-ss[j]][j];
if (i == ss[j]) it[i][j] = it[i][j-1]+1;
else it[i][j] = it[i][j-1];
for (++j; j <= MAX; ++j)
it[i][j] = it[i][j-1];
}
}

int main(void)
{
float f;
Init();
while (scanf("%f", &f) == 1 && fabs(f) > 1e-2)
{
int pos = (int)(f * 100) / 5;
printf("%5.2f%12d\n",f, it[pos][10]);
}

return 0;
}

liliflashfly 2009-03-21
  • 打赏
  • 举报
回复
SOJ....
spirit_sheng 2009-03-21
  • 打赏
  • 举报
回复
以下是代码, 提交去, 哪有上面他们写的那么麻烦啊
[Code=C/C++]
#include <stdio.h>

unsigned int slove(int val, int pos);

int main()
{
while (1)
{
float money;
scanf("%f", &money);
int val = money * 100;
if (val == 0) break;
unsigned int result;
if ((val % 5) != 0) result = 0;
else result = slove(val/5, 0);
printf("%5.2f%12u\n", money, result);
}
return 0;
}

// const int OrgItems[11] = { 10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5 };
const int items[11] = {2000, 1000, 400, 200, 100, 40, 20, 10, 4, 2, 1};
unsigned int rs[1001][10];

unsigned int slove(int val, int pos)
{
if (val < 0) return 0;
if (pos == 10) return 1;
if (rs[val][pos]==0)
rs[val][pos] = slove(val-items[pos], pos) + slove(val, pos+1);
return rs[val][pos];
}
[/Code]
spirit_sheng 2009-03-21
  • 打赏
  • 举报
回复
我说的不是Hash冲突, 而是性能
Hash一般情况下一次定位, 少说情况需要二次或多次Hash
而它的hasThisHash()函数里用循环这么写, 这还是哈希算法吗?


[Quote=引用 13 楼 litaoye 的回复:]
应该没什么问题,只要金额不超过1000000,就不存在hash冲突,不过如果金额超过1000000的话,解的数量可能早就超过Int64了,
而且用递归的话也会溢出。

引用 12 楼 spirit_sheng 的回复:

楼上, 你这Hash这么写问题也太大了吧

[/Quote]

绿色夹克衫 2009-03-21
  • 打赏
  • 举报
回复
应该没什么问题,只要金额不超过1000000,就不存在hash冲突,不过如果金额超过1000000的话,解的数量可能早就超过Int64了,
而且用递归的话也会溢出。

[Quote=引用 12 楼 spirit_sheng 的回复:]
楼上, 你这Hash这么写问题也太大了吧
[/Quote]
绿色夹克衫 2009-03-21
  • 打赏
  • 举报
回复
不好意思,没仔细看,这么做hash性能确实存在很大问题,可以用hashtable或二维数组做.

[Quote=引用 14 楼 spirit_sheng 的回复:]
我说的不是Hash冲突, 而是性能
Hash一般情况下一次定位, 少说情况需要二次或多次Hash
而它的hasThisHash()函数里用循环这么写, 这还是哈希算法吗?


引用 13 楼 litaoye 的回复:
应该没什么问题,只要金额不超过1000000,就不存在hash冲突,不过如果金额超过1000000的话,解的数量可能早就超过Int64了,
而且用递归的话也会溢出。

引用 12 楼 spirit_sheng 的回复:

楼上, 你这Hash这么写问题也太大了吧


[/Quote]
spirit_sheng 2009-03-20
  • 打赏
  • 举报
回复
楼上, 你这Hash这么写问题也太大了吧

enzoo 2009-03-19
  • 打赏
  • 举报
回复
以下是我根据litaoye的方法写的C++代码,通过运行了,自己动手收益匪浅,呵呵

// changes.cpp : Defines the entry point for the console application.
//
/////////////////////////////////////
// by Enzo Yang
////////////////////////////////////
#include "stdafx.h"
#include "stdlib.h"
#include <vector>
#include <time.h>
#include <iostream>
using namespace std;

typedef struct tag_dic
{
int hash;
int value;
} dic;
int getMethods(int,int);
bool hasThisHash(int,int*);
vector<dic> vdHashTable;
int nChanges[] = {10000,5000,2000,1000,500,200,100,50,20,10,5};
int _tmain(int argc, _TCHAR* argv[])
{
int nWhole = 8000;
int res;
//////////////////////////////////////////
clock_t start, finish;
start = clock();
////////////////////////////////////////
if(nWhole % 5 == 0)
{
res = getMethods(0,nWhole);
}
else
{
res = 0;
}
cout << res << endl;
//////////////////////////////////////////
finish = clock();
cout << (double)(finish - start)<< endl; ;
//////////////////////////////////////////
system("pause");
return 0;
}

int getMethods(int nPosition,int nLeft)
{
if(nLeft < 0)
return 0;
if(nLeft == 0)
return 1;
if(nPosition == 10)
return 1;
int hash = nPosition * 1000000 + nLeft;
int nWhere;
if(hasThisHash(hash,&nWhere))
return vdHashTable[nWhere].value;
int res = getMethods(nPosition + 1,nLeft) + getMethods(nPosition,nLeft - nChanges[nPosition]);
dic dItem = {hash,res};
vdHashTable.push_back(dItem);
return res;
}

bool hasThisHash(int hash,int *pnWhere)
{
int length = vdHashTable.size();
for(int i = 0; i < length; i++)
{
if(vdHashTable[i].hash == hash)
{
*pnWhere = i;
return true;
}
}
return false;
}
码农首席 2009-03-19
  • 打赏
  • 举报
回复
想了一下,发现我存在的问题可能不是代码问题,而是我还不能够了解这其中的思想和算法核心的意义。。。。

比如说,这个递推公式:

getcount(position,value) = getcount(position + 1,value) + getcount(position,value - Item[position]);

我不知道为什么这样运算就可以得出答案来。。。。

先等我去想一下哈,如果有问题等下再问。。。。
ialufiac 2009-03-17
  • 打赏
  • 举报
回复
好难
  • 打赏
  • 举报
回复
循环或者递归都可以实现。但是为了规避重复计算的情况发生,一般还是建议自下往上dp(循环控制)
for(i=1;i<=m;i++)
{
for(j=1;j<=n;j++)
//......;
}
绿色夹克衫 2009-03-16
  • 打赏
  • 举报
回复
如果用递归的话,一定要加入一个记录中间状态的hash,否则就会像LS所说的,出现大量的重复运算,
就好像用递归求菲波那切数是的,弄不好是指数级的,我给出的程序的hash部分,实际上就相当于是dp了,
我是通过计算hash,将一个二维数组影射为一维数组,int hashCode = position * 1000000 + value;
其实也可以直接用一个dp[position,value]二维数组保存中间计算的结果。

其实c和c#在解这道题时都差不多,最重要的就是这个递推公式

getcount(position,value) = getcount(position + 1,value) + getcount(position,value - Item[position]);

[Quote=引用 6 楼 GX43AH 的回复:]
我水平实在有限。。。。所以感觉还是比较抽象。。。。
思想可以理解,但对代码实现没什么想法。。。。
关于递推的方法,是否要使用递归函数呢。。?
我现在最大的问题就在于状态转移方程的代码实现。。。
像dp(i-1,j)这类表示,变量i,j是使用循环增加还是递归增加呢。。?
[/Quote]
码农首席 2009-03-13
  • 打赏
  • 举报
回复
我水平实在有限。。。。所以感觉还是比较抽象。。。。
思想可以理解,但对代码实现没什么想法。。。。
关于递推的方法,是否要使用递归函数呢。。?
我现在最大的问题就在于状态转移方程的代码实现。。。
像dp(i-1,j)这类表示,变量i,j是使用循环增加还是递归增加呢。。?
  • 打赏
  • 举报
回复
抽象吗?不啊,含义很清楚~
用前面i种货币来凑足找零的钱数j,只有这几种可能:
第i种货币一张也不用,这样的方案有dp(i-1,j)种;
第i种货币只用1张,这样的方案有dp(i-1,j-V[i])种;
第i种货币用2张,这样的方案有dp(i-1,j-2*V[i])种;
......
第i种货币用j/V[i]张(整数除法),这样的方案有dp(i-1,(j/V[i])*V[i])种.
所以整个下来有
dp(i,j)
=dp(i-1,j)+dp(i-1,j-V[i])+dp(i-1,j-2*V[i])+...+dp(i-1,(j/V[i])*V[i])
=∑dp(i-1,j-t*V[i])(0<=t<=j/V[i])

代码实现的时候用一个二维数组,由边界开始递推,从下标较小的元素逐渐推算出所有下标较大的元素。
码农首席 2009-03-13
  • 打赏
  • 举报
回复
To litaoye:非常抱歉,我忘了说我是学C语言的。。。。有些语句不是很明白是什么意思。。。。

To dlyme:就是那个状态转移方程太抽象了。。。。。。我不知道该如何用代码表示。。。。。

能否详细一点说明。。?
  • 打赏
  • 举报
回复
题目最终是要求解dp(m,n)
加载更多回复(2)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧