盒子中有n张卡片,上面的数字分别为k1,k2,...,kn。你有4次机会,每抽一次,记录下卡片上的数字,再将卡片放回盒子中。如果4个数字的和等于m。则你就赢得游戏,否则就是输。直觉上,赢的可能性太低了。请你给出程序,判断是否有赢的可能性。尽量提高方法的效率。
分析:这个题目,和之前Google的一个概率的题目是类似的,当然解决的方法也是类似的,思路大家可以找找前面的题目,不再赘述。这个题目其实想和大家讲一下思考问题、改进解法的思路。
这个题目最直接的方法就是四重循环遍历,n^4的时间复杂度,比较恐怖,但确实一个很好的起点。不用觉得很丢人,只要我们持续改进即可。假设a,b,c是k1到kn中的三个数字,是否存在d使得,a+b+c+d=m?d在k1到kn中。和题目中的意思是一样的,变换等式如下:d = m - a - b - c
如果满足上式,就是说,要在k1到k2中查找d,即:查找m - a - b - c,找到就存在;找不到,就是不存在。查找有线性查找,二分查找等。四重循环最内一层循环,可以认为是线性的查找,如果想更快一些,可以应用二分查找,而二分查找需要k1到kn是排序的,则先对n个数字进行排序,时间复杂度O(nlogn)。然后最内一层循环,改为二分查找,时间复杂度为O(n^3logn)。所以总的时间复杂度为O(n^3logn)。
经过上面的分析,我们可以得到启发,既然可以提出一个d,那么就可以再提出一个c。将a+b+c+d=m转换为c+d=m-a-b。这样,我们可以枚举k1到kn的两个数的和,然后对n^2个和,进行排序,时间复杂度为o(n^2logn)。然后遍历n^2个和,设每一个和为pair,查找是否存在m-pair,同样二分查找,时间复杂度为O(n^2logn)。总的时间复杂度为O(n^2logn)。
经过上面的层层分析,大家能否发现,对于算法的持续改进,还是有一些窍门的,大家细心领悟。领悟得多了,必会有更大的进步。具体代码如下:
#include<iostream> #include <algorithm> #include<vector> #include <set> using namespace std; double digtalGame1(vector<int> num,int m)//方法一:四层循环 { sort(num.begin(),num.end());//先排序 int i1,i2,i3,length = num.size(),count = 0; for(i1 = 0 ;i1 < length;++i1) { for(i2 = 0;i2 < length;++i2) { for(i3 = 0;i3 <length;++i3) { int d = m - (num[i1]+num[i2]+num[i3]); int start = 0,end = length-1; while(start <= end)//二分查找对有重复数字的情况统计的结果会偏小 { int mid = start + ((end - start) >> 1); if(num[mid] < d)start = mid +1; else if(num[mid] > d)end = mid -1; else { count ++; cout << num[i1] << " " << num[i2] << " " << num[i3] << " " << d << endl; break; } } } } } return (1.0*count)/(length * length * length * length); } double digtalGame2(vector<int> num,int m)//方法二:先求两个数的和 { int i1,i2,length = num.size(),count = 0; multiset<int> sum; for(i1 = 0;i1 < length;++i1)//计算所有两个数的和 { for(i2 = 0;i2 < length;++i2) { sum.insert(num[i1]+num[i2]); } } multiset<int>::iterator iter = sum.begin(); for(;iter != sum.end();iter++)//遍历和组成的集合,查看另一半的和是否存在,也可以将所有的n^2个和排序,然后用两个指针,但两个指针可以交叉,即必须走到两头 { int otherSum = m - *iter; count += sum.count(otherSum); } return (1.0*count)/(length*length*length*length); } int main() { int n,m,i; while(cin >> n >> m) { vector<int> num(n); for(i = 0;i < n;i++)cin >> num[i]; cout << digtalGame1(num,m) << endl; cout << digtalGame2(num,m) << endl; } }