210 likes | 380 Views
习 题课 3. 赵嘉明 . Topic. 了解仿函数( functor ) 讨论 bfs&dfs 网上作业习题. 何谓仿函数. 仿函数,顾名思义,就是具有函数的性质但是本身又不是函数的四不像(是一个 class ) In essential ,我们把在重载过 operator() 的类叫做仿函数。 For example : class FunctionObject { public: void operator()( int a,int b) {…………;} }.
E N D
习题课 3 赵嘉明
Topic • 了解仿函数(functor) • 讨论bfs&dfs • 网上作业习题
何谓仿函数 • 仿函数,顾名思义,就是具有函数的性质但是本身又不是函数的四不像(是一个class) • In essential,我们把在重载过operator()的类叫做仿函数。 For example: class FunctionObject { public: void operator()(inta,int b) {…………;} } In this way. 我们可以像使用普通函数一样使用这种类。 FunctionObjectfunctor; functor(a,b); 上述操作会自动调用重载过的operator()完成函数操作。
仿函数可以让程序变的简洁 Class average {private: intnum; int sum; public: average():num(0),sum(0) {} void operator()(intelem) { num++; sun+=elem;} double value() { return (double)(sum)/(double)(num);} }; in main….. 我们使用algorithm中的for_each()函数,它对区间内每一个元素执行相同的函数操作 vector<int> coll; initialize coll……. average result=for_each(coll.begin(),coll.end(),average()); cout<<average.value(); 我们可以把多个操作整合在一个仿函数中,让main函数一目了然,而且STL中提供了许多这样的借口,可以方便我们的使用,例: 此处通过调用average类的构造函数,产生了一个临时的average类对象。 Algorithm中的for_each()函数具有一个仿函数类型的返回值,该仿函数类型与第三个参数保持一致。
C++STL中内置的仿函数 • 我们需要include <functional>来使用它们。 • plus minus multiplies divides modulus negate • greater less greater_equalless_equal • logical_andlogical_orlogical_not,
将仿函数作为set和map的排序准则 • Set默认为元素从小到大排序,map默认为按key值从小到大排序,但是我们可以根据应用需要用仿函数更改默认值,让它们变的更灵活。 • 在set的定义文件中,set有三个模板参数 Template<class T, class Compare = less<T>, class Allocator = allocator<T> > Class set { …….. } 其中第二个模板参数就是它的排序准则,具有less<T>的仿函数默认值,我们可以通过指定其它仿函数改变它. set<int ,greater<int> > intset; 当然你也可以自己撰写仿函数,但是为了被STL识别,我们需要加上一些接口。
将仿函数作为算法的逻辑判断式 • count_if (iterator iteratorfunctor); remove_if(iterator iteratorfunctor); find_if(iterator iteratorfunctor); 此处的仿函数具有bool型返回值,我们将对使functor执行结果为true的元素进行前面的操作。
仿函数接配器—化二元仿函数为一元 对于modulus<int>() ,应该有两个参数,该函数计算第一个参数对第二个参数的取模值,并返回。 可是如果我们想要将一个容器中偶数的值都删除,应该如何做? 显然我们应该把对二取模,结果等于0的值删除,但remove函数中,我们实际上只给仿函数传了一个值。 remove_if(iterator, iterator, modulus<int>() ) 为此我们定义了仿函数接配器,让它们嵌套在仿函数的外面,来解决参数不足的问题。 bind1st(functor,指定的第一参数) bind2nd(functor, 指定的第二参数) not1(functor) //对仿函数中计算的结果反相操作 即false->true true->false 因此我们可以对上述函数进行如下修改 remove_if(iterator , iterator , not1(bind2nd(modulus<int>(),2))); 将该区间内的一个元素传给仿函数作为参数,但却无法传递关键的取模数2
Another example: 找到第一个小于5的数; vector<int>::iterator pos; pos=find_if(coll.begin(),coll.end(),bind2nd(less<int>(),5)); 或者 pos=find_if(coll.begin(),coll.end();bind1st(greater<int>(),5));
BFS & DFS DFS:主要使用递归嵌套来实现 dfs(n) { if(达到满足的条件) 给出一个解决办法; if(满足剪枝的条件) 进行剪枝; 对数据进行修改,准备下次递归; dfs(n+1); 将更改的数据全部更改回来,恢复原始状态; }
BFS:用一个队列实现 bfs() { queue.enqueue(初始结点); while(!queue.empty()) { 搜索与初始结点有直接关系的那些结点; if(满足条件) 给出解答办法,return if(该节点以前入队过) 不做处理 if(该节点没入队过) 让其入队 } } (第五章中树的层次遍历就是简化了的bfs)
1 4 3 3 2
3 2 3 1 3 2 3 3 2
BFS 1.能较快找出问题的最优解。 2.需要大量内存空间记录已探索过的结点从而实现剪枝 • DFS 1.dfs通过递归调用,时间相对较慢,但不需要占用很多内存空间。 2.Dfs剪枝很重要(最优性和可行性),通过剪枝可以节省大量的时间。
The Clocks 1.对每一种变换重复四次相当于没操作过。 2.结果和操作的顺序没有关系。 剪枝方案: 1.对每种变换进行次数进行记录,最多只重复三次。 2.从第一种变换开始试验,再试验接下去的变换,变换种类是单调不减的。 3.最优性剪枝。
用way[n]记录变化的编号 用use[n]记录每次变化执行的次数 dfs(int n) { if(变化次数大于已有的最优解) return; if(得到可行解) 将其设为最优 return; for(int i=way[n-1];i<=9;++i) { if(use[i]==3) continue; use[i]++; 进行变化 ; way[n]=i; dfs(n+1); use[i]--; 进行逆变化;} }
ariprog 1.将所有的p^2+q^2全部计算出来,保存在num数组里。 并用一个exist数组记录它们是否存在(hash table) num数组大小不超过 250^2 exist数组不超过 2*250^2 2.将num数组进行排序(merge sort| quick sort) 3.search算法: Search() { for(int i=0;i<numsize;++i) { if(num[i]=num[i-1]) continue; for(int j=0;j<n;++j) {if(!exist[m[i]+difference*j]) break; if(j=n-1) 给出可行解; }} } 其中difference取值范围从1~num[maxnum-1]/(n-1)+1