程序崩溃,树太大
Program crashes, Tree too large
我正在尝试将此问题作为练习:
这是一个盒子中{50,25,10,5,1}的硬币集。编写一个程序来查找可以通过对硬币进行分组来创建1美元的方法数量。
我的解决方案涉及制作一棵树,每个边缘具有上面的一个值之一。然后,每个节点将容纳硬币的总和。然后,我可以填充这棵树,寻找总计100的叶子。所以这是我的代码
class TrieNode
{
public:
TrieNode(TrieNode* Parent=NULL,int sum=0,TrieNode* FirstChild=NULL,int children=0, bool key =false )
:pParent(Parent),pChild(FirstChild),isKey(key),Sum(sum),NoChildren(children)
{
if(Sum==100)
isKey=true;
}
void SetChildren(int children)
{
pChild = new TrieNode[children]();
NoChildren=children;
}
~TrieNode(void);
//pointers
TrieNode* pParent;
TrieNode* pChild;
int NoChildren;
bool isKey;
int Sum;
};
void Populate(TrieNode* Root, int coins[],int size)
{
//Set children
Root->SetChildren(size);
//add children
for(int i=0;i<size;i++)
{
TrieNode* child = &Root->pChild[0];
int c = Root->Sum+coins[i];
if(c<=100)
{
child = new TrieNode(Root,c);
if(!child->isKey) //recursively populate if not a key
Populate(child,coins,size);
}
else
child = NULL;
}
}
int getNumKeys(TrieNode* Root)
{
int keys=0;
if(Root == NULL)
return 0;
//increment keys if this is a key
if(Root->isKey)
keys++;
for(int i=0; i<Root->NoChildren;i++)
{
keys+= getNumKeys(&Root->pChild[i]);
}
return keys;
}
int _tmain(int argc, _TCHAR* argv[])
{
TrieNode* RootNode = new TrieNode(NULL,0);
int coins[] = {50,25,10,5,1};
int size = 5;
Populate(RootNode,coins,size);
int combos = getNumKeys(RootNode);
printf("%i",combos);
return 0;
}
问题在于,树是如此之大,以至于几秒钟后,该程序崩溃了。我正在使用8GB RAM的Windows 7(Quad Core)运行此操作。一个粗略的计算告诉我我应该有足够的记忆。
我的计算不正确?操作系统是否限制了我可以访问多少内存?我可以在仍在使用此解决方案时修复它吗?
所有反馈都将不胜感激。谢谢。
edit1:我已经证实了上述方法是错误的。通过尝试建造一组只有1个硬币的树。 硬币[] = {1};
我发现该算法仍然失败。阅读了Lenik和JoãoMenighin的帖子后我提出了这个解决方案,将两个想法都联系在一起以制定递归解决方案采用任何大小的数组
//N is the total the coins have to amount to
int getComobs(int coins[], int size,int N)
{
//write base cases
//if array empty | coin value is zero or N is zero
if(size==0 || coins[0]==0 ||N==0)
return 0;
int thisCoin = coins[0];
int atMost = N / thisCoin ;
//if only 1 coin denomination
if(size==1)
{
//if all coins fit in N
if(N%thisCoin==0)
return 1;
else
return 0;
}
int combos =0;
//write recursion
for(int denomination =0; denomination<atMost;denomination++)
{
coins++;//reduce array ptr
combos+= getComobs(coins, size-1,N-denomination*thisCoin);
coins--;//increment array ptr
}
return combos;
}
感谢所有反馈
树解决方案对于此问题是完全错误的。这就像抓住10e6老虎,然后放开所有东西,只有一个,只是因为您需要一个老虎。时间和记忆消耗 - 99.999%的节点是没有用的,应该首先忽略。
这是另一种方法:
- 请注意,您不能赚一美元,而无法容纳两个以上的50美分
- 再次通知您无法赚钱以包含超过四个25美分的硬币
- 注意...(您明白了吗?)
那么您的解决方案很简单:
for( int fifty=0; fifty<3; fifty++) {
for( int quarters=0; quarters<5; quarters++) {
for( int dimes=0; dimes<11; dimes++) {
for( int nickels=0; nickels<21; nickels++) {
int sum = fifty * 50 + quarters * 25 + dimes * 10 + nickels * 5;
if( sum <= 100 ) counter++; // here's a combination!!
}
}
}
}
您可能会问,为什么我不对单一的硬币做任何事情?答案很简单,一旦sum
小于100,其余的则充满1美分。
ps。希望此解决方案不是太简单=)
好吧,这不是一个完整的答案,而是可能对您有所帮助。您可以尝试执行(我所说的)理智检查。对于创建的每个节点,将static
计数器放入TrieNode
中,然后查看其生长的大小。如果您进行了一些计算,则应该能够确定它是否达到一些疯狂的值。
系统可以限制可用的内存,但是这确实很奇怪。通常,用户/管理员可以为某些目的设置此类限制。这经常在专用的多用户系统中发生。另一件事可能是在64位Windows环境中拥有32位应用程序。然后MEM限制将为4GB,但是这也非常奇怪。我认为没有被操作系统限制是一个问题。
在旁注上。我希望您确实意识到,您有点用此代码击败了所有面向对象的编程概念:)。
我需要更多时间来分析您的代码,但是现在,我可以说这是一个经典的动态编程问题。您可能在这里找到一些有趣的文本:
http://www.algorithmist.com/index.php/coin_change
和这里
http://www.ccs.neu.edu/home/jaa/csg713.04f/information/handouts/handouts/dyn_prog.pdf
是找到解决方案的一种更简单的方法:
#include <iostream>
#include <cstring>
using namespace std;
int main() {
int w[101];
memset(w, 0, sizeof(w));
w[0] = 1;
int d[] = {1, 5, 10, 25, 50};
for (int i = 0 ; i != 5 ; i++) {
for (int k = d[i] ; k <= 100 ; k++) {
w[k] += w[k-d[i]];
}
}
cout << w[100] << endl;
return 0;
}
(链接到IDEONE)
这个想法是通过添加更大的面额中的硬币来逐步构建进行更改的方法。外循环的每次迭代都会贯穿我们已经拥有的结果,对于可以使用新添加的硬币构建的每个数量,添加了可以构建当前硬币的值较小的组合方式的数量。例如,如果当前硬币是5
,并且当前数量为7
,则该算法查找可以构建2
的方式的数量,并将其添加到可以构建7
的方式中。如果当前硬币是25
,并且当前金额为73
,则该算法将构造48
(73-25
)的方法数量查找到先前找到的构造73
的方法数量。最后,w[100]
中的数字代表了赚钱的方法数(292种方法)。
我确实相信有人必须放置最有效,最简单的可能实现是对Lenik答案的改进:
内存:常数
运行时间:将100作为 n ,然后运行时间大约是O(n(lg(n)))&lt; - i I不确定
for(int fifty=0; fifty <= 100; fifty+=50)
for(int quarters=0; quarters <= (100 - fifty); quarters+=25)
for(int dimes=0; dimes <= (100 - fifty - quarters); dimes+=10)
counter += 1 + (100 - fifty - quarters - dimes)/5;
我认为这可以在恒定时间内解决,因为任何序列总和都可以用线性公式表示。
问题可能是无限递归。您不会在任何位置和循环使用C&lt; = 100
上运行c编辑1:我不确定
是否int c = Root->Sum+coins[i];
实际上将其超过100。请验证
编辑2:我错过了正确初始化的总和,并在下面的评论中进行了纠正。
编辑3: debug - 您可以做的另一件事是,为此树编写一个打印功能,或者随着在现有代码中更深入的过程中的每个级别上打印功能。添加一个计数器,该计数器在总计10次迭代后终止循环。印刷品会告诉您是否获得垃圾值或C逐渐朝着正确的方向增加。
- 提升 ASIO Async_receive崩溃程序
- 类指针方法崩溃程序
- get_body来自 IHTMLDocument2 崩溃程序
- 是否有可能存在不会崩溃程序的内存问题
- 调试运行时出现奇怪的崩溃程序(Eclipse C++)
- 儿童对话框 - setWindowTexta或sendmessagea崩溃程序-MFC
- 顶点阵列GLFW崩溃C 程序
- 将值分配给Float数据类型崩溃程序
- OpenCV-将变量添加到类成员崩溃程序
- boost::interprocess::managed_shared_memory 崩溃程序
- 将枚举值分配给整数崩溃程序
- SDL 1.2 -> SDL 2.0(崩溃程序)
- 空析构函数崩溃程序:C++
- 冒泡排序崩溃程序c++
- Ofstream关闭崩溃程序
- 删除字符* 崩溃程序
- c++矢量擦除崩溃程序
- Lua_getglobal崩溃程序
- 通过Copy Constructor创建的e2打印崩溃程序
- RNG崩溃c++程序