【算法】穷举法解搜狐2012年的一道笔试题
这道笔试题,我是从chinaunix论坛上看到的,原帖网址为:bbs.chinaunix.net/thread-3604016-1-1.html,是网友snowboy9859发的。原帖是:“有N个正实数(注意是实数,大小升序排列)x1,x2,……,xN,另有一个实数M。需要选出若干个x,使这几个x的和与M最接近。请描述实现算法,并指出算法复杂度。”
看到问题之后的刹那间,脑海里浮想联翩,混混沌沌,思路很不清晰。现在回想一下,当时自己脑子里是在设想符合题目要求的各种情况。也就是说,我的潜意识的第一反应是用穷举法解这道题,可是要考虑的情况似乎太多了,所以思路就乱了。
然后,我开始认真察看网友的回复,果然,在很靠前的位置,就有网友提出用穷举法,但他说得很概括。回复楼主的网友很多,绝大多数都提出了自己的想法,其中很多人提到这道题类似“0/1背包问题”,可以借助解“0/1背包问题”的算法解这道题。所以我又到其他网站上搜索关于“0/1背包问题”的知识,搜到了很多,其中很多大侠都给出了自己的源代码。博客园上的一位大侠张玉彬(www.cnblogs.com/jillzhang/archive/2007/10/06/915035.html#2242617)在讲解“0/1背包问题”之前,先提到了穷举法:“这种问题最简单的方法就是找出这个向量的所有子集,如同找出幂集中的子集一样,但这种遍历的方法恐怕并不会被聪明的我们所使用”。“幂集”一词,本来是数学上的,对我来说很少听到,可它一下子让我想起了《数据结构》上有一道题是解一个集合的幂集的(怪不得老师向他的每一个学生都推荐这本书(C语言版,严蔚敏、吴伟民编著,清华大学出版社)~~)。
看完了张玉彬的那篇文章(其实,在此之前,我已经看了几篇其他网站上的相关文章),我学到了两种方法解那道搜狐笔试题,一个是穷举法,另一个是解“0/1背包问题”的方法(当然,用穷举法也能解“0/1背包问题”,但关于“0/1背包问题”有更经典的独特方法)。现在,我要用穷举法来解这道题,因为现在我只实现了穷举法。关于“0/1背包问题”的更好的解法,我还得再回味一下,我觉得自己理解的还不够深入。
穷举法,也就是数学上求集合幂集的方法,用试探和回溯的搜索技术求解。关于幂集和回溯法,上文提到的书中第6.7讲得挺好的。
穷举法要列举2^N种情况,希望大家伙儿多多提出自己的想法,找出更高效的算法。穷举法程序源代码如下:“
#include <iostream> using std::cout; using std::endl; using std::cin; #include <vector> // list class-template definition using std::vector; #include <algorithm> using std::for_each; #include <numeric> // accumulate is defined here using std::accumulate; vector<float> nearB; float M = 0; float nearest = 0; bool theFirstB = true; void GetNearestPowerSet(int i, vector<float> &A, vector<float> &B) { // 线性表A表示集合A, 线性表B表示幂集Rou(A)的一个元素. // 第一次调用本函数时, B为空表, i=1. if (i > A.size()) { // 当前B即为Rou(A)的一个元素, 为了完成目标, 要进行以下操作: // 1. 求出B中所有元素的和sumB; // 2. 求出sumB-M的差值的绝对值e; // 3. if e<nearest then (nearest=e; and 将B暂存); // 注: 变量nearest中存放的是所有所求得的sumB与M差值的绝对值的最小值 float sumB = accumulate(B.begin(), B.end(), 0); float e = abs(sumB-M); if (theFirstB) { theFirstB = false; nearest = e; nearB = B; } else { if (e < nearest) { nearest = e; nearB = B; } } } else { float x = A.at(i-1); B.push_back(x); // 取第i个元素, 注vector元素下标从0算起: GetNearestPowerSet(i+1, A, B); B.pop_back(); // 舍第i个元素 GetNearestPowerSet(i+1, A, B); } } void OutputElements(float elements) { cout << elements << ' '; } void PrintResult(float value, vector<float> &theVector) { cout << "the nearest different value with " << M << " is: " << value << '\n'; cout << "the elements is: "; for_each(theVector.begin(), theVector.end(), OutputElements); cout << endl; } int main() { int N = 0; vector<float> A; vector<float> B; float element = 0; cout << "请输入目标值(float型): "; cin >> M; cout << "请输入float型元素(输入0结束): "; while (cin>>element, element) { A.push_back(element); } GetNearestPowerSet(1, A, B); PrintResult(nearest, nearB); return 0; }
”。