一道笔试题:捞鱼问题
题目:20个桶,每个桶中有10条鱼,用网从每个桶中抓鱼,每次可以抓住的条数随机,每个桶只能抓一次,问一共抓到180条的排列有多少种 (也可求概率)。分析一下这道题其实可以这样理解,假设我已经抓到了180条鱼,而这180条鱼来自20个桶中,反过来就是我们将这180条鱼放到20个桶中且要保证每个桶的鱼在(0-10)之间,有多少种方法。假设我们在前i个桶中抓取了k(0<=k<=10*i)条鱼,那么抓取180条鱼的排列种数就等于在剩下的(20-i)个桶中抓取(180-k)条鱼的方法加上前i个桶中抓取k条鱼的方法。再想一下由于总共有200条鱼,我们抓走了180条鱼,那么还剩下多少条呢?显然是20条。再用上面的思维,我们就可以这样想,将20条鱼放回这20个桶中满足每个桶中的鱼(0-10),有多少种放法。这里当然是因此了一个排列问题而不是组合问题,因为我第一个桶放1条鱼、第2个桶不放与第一个桶不放、第二个桶放1条鱼,是两种的不同的放法。这样分析下来,感觉就是一个跳台阶问题:一个梯子有20步,每次可以跳0-10步,20步跳完多少中跳法(可选择不跳)。编程思想思想其实跟完全背包问题类似,我们首先考虑第20个桶放多少鱼,那么我们就可以减去最后一个桶放的鱼数,再去考虑前面19个桶怎么放剩下的鱼。这样形成了一个递归,我们就可以很快写出如下代码。[cpp]//bucket,表示当前考虑到第几个桶,当然首先考虑第20个桶//fishN,表示当前有多少鱼//返回值表示有多少中方法int Func(int bucketN,int fishN){if(bucketN<0 || fishN<0)return 0;//错误处理if(bucketN == 1){if(fishN>=0 && fishN<=10)//第1个桶里面放0-10的鱼,方法只有一种return 1;else{return 0; //其他用0种方法}}int sum = 0;for(int i=0; i<=10; ++i){sum += Func(bucketN-1,fishN-i);//考虑前面bucket-1的组合,同时减掉当前放的鱼}return sum;}递归优化上面的代码,其实有很多重复计算。下面我们简单的分析一下:F(20,20) = F(19,19)+ F(19,18).......; //考虑第20个桶放一条鱼和两条鱼的情况F(19,19) = F(18,17)+.....;//第19个桶放2条鱼 ①F(19,18) = F(18,17)+.....;//第19个桶放1条鱼 ②我们发现①和②都调用了F(18,17),但是它们确实各算各的,这样就存在着很多类似的重复计算。该递归树,基本全是重复的点,这样时间复杂度被拖得很高。那么,我们只要设计一个数组来把计算好的值保存下来,这样避免了很多重复的计算。代码如下:[cpp]//bucket,表示当前考虑到第几个桶,当然首先考虑第20个桶//fishN,表示当前有多少鱼//返回值表示有多少中方法int dp[21][200] = {0};int FuncOptimize(int bucketN,int fishN){if(bucketN<0 || fishN<0)return 0;if(bucketN == 1){if(fishN>=0 && fishN<=10)return 1;else{return 0;}}if(dp[bucketN][fishN] == 0){ //这个子过程没有被计算我们采取调用递归计算for(int i=0; i<=10; ++i){dp[bucketN][fishN] += FuncOptimize(bucketN-1,fishN-i);}}return dp[bucketN][fishN];}下面我们用我们熟悉的斐波拉契序列做一个实验,我们同样的优化方式来做,看下程序跑得快了多少?[cpp]#include<iostream>#include <windows.h>using namespace std;long long fibonacci[100];long long Fibonacci(int n){if(n == 0)return 1;if(n == 1)return 1;return Fibonacci(n-1) + Fibonacci(n-2);}long long FibonacciOptimize(int n){if(n == 0)return 1;if(n == 1)return 1;if(fibonacci[n] == 0){fibonacci[n] = FibonacciOptimize(n-1) + FibonacciOptimize(n-2);}return fibonacci[n];}int main(){DWORD seconds = GetTickCount();cout<<Fibonacci(40)<<endl;cout<<"优化之前:"<<(GetTickCount() - seconds)<<"ms"<<endl;seconds = GetTickCount();cout<<FibonacciOptimize(40)<<endl;cout<<"优化之后:"<<(GetTickCount() - seconds)<<"ms"<<endl;system("pause");return 0;}这个结果很是满意啊。动态规划实现我们知道这种自顶向下的递归,都可以转换为动态规划自底向上的思想。就是我们递归的值是有下面的值一步步垒起来的,那么我只需要一开始就把下面的值算好,保持在那里,然后再算上面的时候,直接拿过来用,这就很快了。据我了解,背包问题的编程思路跟这道题是一摸一样,只不过背包走到第i种物品的时候,第i种物品的重量已经固定,而我们这道题是第i个桶取值是一个范围,那我们只要把这里范围都扫一遍就好了。[cpp]#include<iostream>#include <windows.h>using namespace std;int dp[21][200] = {0};int FuncDp(int bucketN,int fishN){int i,j,k;for(i=0; i<=10; ++i)dp[1][i] = 1;for(i=2; i<=bucketN; ++i){for(j=0; j<=fishN; ++j){for(k=0; k<=10&&j-k>=0; ++k){//可以取0-10 补充:软件开发 , C++ ,
上一个:将数字字符串转化为整数
下一个:灰度直方图均衡化(上)
- 更多C/C++疑问解答:
- 关于c++的cout输出的问题。
- 在学校里学过C和C++,不过学的很一般,现在自学C#,会不会很难?
- 全国计算机二级C语言笔试题
- 已知某树有2个2度结点,3个3度结点,4个4度结点,问有几个叶子结点?
- c++数据结构内部排序问题,整数排序
- 2012九月计算机二级C语言全国题库,,急求急求
- 如果assert只有一个字符串作为参数,是什么意思呢?
- C语言中,哪些运算符具有左结合性,哪些具有右结合性,帮忙总结下,谢谢了!
- 为什么用结构体编写的程序输入是,0输不出来啊~~~
- 将IEEE—754的十六进制转化为十进制浮点类型,用C或C++都行,多谢各位大侠啊,非常感谢!
- 为什么这个程序求不出公式?
- 这个链表倒置的算法请大家分析下
- c语言函数库调用
- C语言unsigned int纠错
- C语言快排求解啊