挑战程序设计竞赛-初出茅庐(最基础的“穷竭搜索”)
2016-07-31 22:29
288 查看
搜索是在程序设计竞赛章、以后的项目中用到的最多的算法思想。原则上,任何的问题都可以搜索解决,但是由于计算机性能的限制,必须找到更加优秀的算法来解决相应的问题。
穷竭搜索是将所有的可能性罗列出来,从中寻找答案。主要是深度优先搜索(DFS)、宽度优先搜索(BFS)、A*算法。主要介绍DFS、BFS。
递归函数:
在函数运行中,再次调用自身的行为叫做递归。主要是递归条件和递归出口。在编写递归函数时,不用特意去考虑函数的运行过程,如果理解不够深刻,会使自己陷入矛盾中,只要判断递归调用和写好递归出口就行了。
Test1:阶乘函数。N! = N x (N-1)! 。
递归过程如下:
斐波那契数列
定义fib(0) = 0; fib(1) = 1;(递归出口);fib(n) = fib(n-1) + fib(n-2) (循环体)[n>1]。
看一下fib(10)的递归过程,可以看出有很多次的重复计算,这样就会导致程序运行很慢,解决的方法一种记做记忆化搜索和动态规划的技术,或者叫做递推。
基础数据结构之栈
栈(stack)是一种先进后出的数据结构,支持pop和push两种操作。Push是放入一个元素到栈顶,pop是从栈顶取出一个元素并删除。栈可以简单的用数组实现。C++、Java中有标准库,例如C++中的stack。Top()取出栈顶元素;pop()删除栈顶元素;push()放入一个元素到栈顶。
Stack的例子;
基础数据结构之队列
队列是一种先进先出的数据结构,也可以用数组简单的实现,数组实现是可以使用数组构造循环队列。C++中的queue队列类型。front()从队头取出一个元素,push(),从队尾进入一个元素;pop(),移除队头元素(出队)。
queue例子:
深度优先搜索dfs:
深度优先搜索是搜索的手段之一。它从某个状态开始不断的转移到下一个状态,直到找到解或者无法继续就返回或者做适当的处理。根据dfs的特点,采用递归方式比较的简单。
Test3 部分和问题
给定整数 a1、a2、。。。、an,判断是否可以从中选出若干个数,使它们的和恰好为K。
限制条件:1 <= n <= 20 ; -1e8 <= k,ai <= 1e8。
样例:
输入:
n = 4
a = {1,2,4,7}
k = 13
输出:Yes {13 = 2 + 4 + 7}。
若k = 15 ,则输出 No。
从ai开始按顺序决定每个数加或者不加,在全部n个数都决定后在判断它们的和是否是K即可。状态数是2^(n+1),所以时间复杂度是O(2^n)。
例题2:联通的个数(Lake Counting)(POJ 2386)
http://poj.org/problem?id=2386
从任意的W开始,dfs搜索与其相邻的八个方向的W,并将其变成 . 。每一次开始就记录加一个联通块的个数。
宽度优先搜索bfs:
宽度优先搜索总是先搜索距离初始状态最近的状态。它的执行过程是:当前状态 一步转移能到达的所有状态 2步转移能到达的所有状态 3步转移能到达的所有状态 ……. n步转移能到达的所有状态(或者到达目的状态)。
对于同一个状态,宽度优先搜索只经过一次,因此时间复杂度为O(状态数X转移方式)。Dfs(隐式)用到了栈,bfs用到了队列。
Bfs的转移过程:
最后,搜索一定一学会用剪枝,就是在搜索过程中,对于一定不满足的状态不在继续往下搜索,而是直接结束,有助于优化时间。
穷竭搜索是将所有的可能性罗列出来,从中寻找答案。主要是深度优先搜索(DFS)、宽度优先搜索(BFS)、A*算法。主要介绍DFS、BFS。
递归函数:
在函数运行中,再次调用自身的行为叫做递归。主要是递归条件和递归出口。在编写递归函数时,不用特意去考虑函数的运行过程,如果理解不够深刻,会使自己陷入矛盾中,只要判断递归调用和写好递归出口就行了。
Test1:阶乘函数。N! = N x (N-1)! 。
int fact(int n){ if (n == 0) return 1; return n * fact(n - 1); }
递归过程如下:
斐波那契数列
定义fib(0) = 0; fib(1) = 1;(递归出口);fib(n) = fib(n-1) + fib(n-2) (循环体)[n>1]。
int fib(int n){ if (n <= 1) return n; return fib(n - 1) + fib(n - 2); }
看一下fib(10)的递归过程,可以看出有很多次的重复计算,这样就会导致程序运行很慢,解决的方法一种记做记忆化搜索和动态规划的技术,或者叫做递推。
int dp[MAXN + 1]; int fib(int n){ if(n <= 1) return n; if(dp != 0) return dp ; return dp = fib(n - 1) + fib(n - 2); }
基础数据结构之栈
栈(stack)是一种先进后出的数据结构,支持pop和push两种操作。Push是放入一个元素到栈顶,pop是从栈顶取出一个元素并删除。栈可以简单的用数组实现。C++、Java中有标准库,例如C++中的stack。Top()取出栈顶元素;pop()删除栈顶元素;push()放入一个元素到栈顶。
Stack的例子;
#include<cstdio> #include<iostream> #include<stack> using namespace std; int main() { stack<int> s; s.push(1); s.push(2); s.push(3);//从栈顶压入1,2,3 cout<<s.top()<<endl; //输出3 s.pop(); //移除3 cout<<s.top()<<endl; //输出2 s.pop(); //移除2 cout<<s.top()<<endl; //输出1 s.pop(); //移除1 return 0; } 注:s.size();//栈的大小。 s.empty();//判断栈是否是空
基础数据结构之队列
队列是一种先进先出的数据结构,也可以用数组简单的实现,数组实现是可以使用数组构造循环队列。C++中的queue队列类型。front()从队头取出一个元素,push(),从队尾进入一个元素;pop(),移除队头元素(出队)。
queue例子:
#include<cstdio> #include<iostream> #include<queue> using namespace std; int main() { queue<int> q; q.push(1); q.push(2); q.push(3);//从队尾进入1,2,3 cout<<q.front()<<endl; //输出1 q.pop(); //移除1 cout<<q.front()<<endl; //输出2 q.pop(); //移除2 cout<<q.front()<<endl; //输出3 q.pop(); //移除3 return 0; }
深度优先搜索dfs:
深度优先搜索是搜索的手段之一。它从某个状态开始不断的转移到下一个状态,直到找到解或者无法继续就返回或者做适当的处理。根据dfs的特点,采用递归方式比较的简单。
Test3 部分和问题
给定整数 a1、a2、。。。、an,判断是否可以从中选出若干个数,使它们的和恰好为K。
限制条件:1 <= n <= 20 ; -1e8 <= k,ai <= 1e8。
样例:
输入:
n = 4
a = {1,2,4,7}
k = 13
输出:Yes {13 = 2 + 4 + 7}。
若k = 15 ,则输出 No。
从ai开始按顺序决定每个数加或者不加,在全部n个数都决定后在判断它们的和是否是K即可。状态数是2^(n+1),所以时间复杂度是O(2^n)。
//输入 int a[MAX_N],n,k; bool dfs(int I,int sum){ //已经判断了前n项,比较sum = k ? if(i==n) return sum==k; //不使用ai if(dfs(i + 1, sum)) return true; //使用ai if(dfs(i + 1, sum + a[i] ) return true; //无论是否加上ai都不能组合成k return false; } int main() { if(dfs(0, 0)) printf(“Yes\n”); else printf(“No\n”); return 0; }
例题2:联通的个数(Lake Counting)(POJ 2386)
http://poj.org/problem?id=2386
从任意的W开始,dfs搜索与其相邻的八个方向的W,并将其变成 . 。每一次开始就记录加一个联通块的个数。
//解决代码 int N,M; char map_m[MAX_N][MAX_N]; void dfs(int x,int y){ map_m[x][y] = '.'; //循环遍历八个方向 for(int dx=-1;dx<=1;dx++){ for(int dy=-1;dy<=1;dy++){ int nx=x+dx, ny=y+dy; if(0<=nx&&nx<N&&0<=ny&&ny<M&&map_m[nx][ny]=='W') dfs(nx,ny); } } return ; } void solve(){ int res = 0; for(int i=0;i<N;i++){ for(int j=0;j<M;j++){ if(map_m[i][j]=='W'){ dfs(i,j); res++; } } } cout<<res<<endl; }
宽度优先搜索bfs:
宽度优先搜索总是先搜索距离初始状态最近的状态。它的执行过程是:当前状态 一步转移能到达的所有状态 2步转移能到达的所有状态 3步转移能到达的所有状态 ……. n步转移能到达的所有状态(或者到达目的状态)。
对于同一个状态,宽度优先搜索只经过一次,因此时间复杂度为O(状态数X转移方式)。Dfs(隐式)用到了栈,bfs用到了队列。
Bfs的转移过程:
全排列问题与dfs、C++STL next_permutation函数 bool used[MAXN]; int perm[MAXN]; void permutation(int pos, int n){ if(pos == n){ for(int i=0;i<n;i++) cout<<perm[i]; cout<<endl; return ; } for(int i=0;i<n;i++){ if(!used[i]){ perm[pos] = i+1; //i已经被使用,标记状态 used[i]=true; permutation(pos+1,n); //返回之后把标记复位 used[i]=false; } } return ; } -------------------------------------------------------------------- for(int i=0;i<n;i++) perm[i]=i+1; do{ for(int i=0;i<n;i++) cout<<perm[i]; cout<<endl; }while(next_permutation(perm, perm+n));
最后,搜索一定一学会用剪枝,就是在搜索过程中,对于一定不满足的状态不在继续往下搜索,而是直接结束,有助于优化时间。
相关文章推荐
- poj3176 基础的动态规划算法 <挑战程序设计竞赛>
- poj2385 基础的动态规划算法 <挑战程序设计竞赛>
- 挑战程序设计竞赛 2.1 最基础的“穷竭搜索”
- poj2229 基础的动态规划算法 <挑战程序设计竞赛>
- 挑战程序设计竞赛 2.1迷宫的最短路径
- 【坐标离散化】 挑战程序设计竞赛
- 挑战编程 程序设计竞赛训练手册-1.6.8 澳大利亚投票(Australian Voting)
- 挑战程序设计竞赛(第2版)》
- 挑战编程 程序设计竞赛训练手册-1.6.5 图形化编辑器(Graphical Editor)
- 挑战程序设计竞赛选录:1
- 挑战程序设计竞赛 2.1部分和问题
- 挑战编程 程序设计竞赛训练手册-1.6.4 液晶显示屏(LC-Display)
- ACM Property Distribution(挑战程序设计竞赛)
- 系统性训练,励志刷完挑战程序设计竞赛-代码整理68~103【初级篇】
- 系统性训练,励志刷完挑战程序设计竞赛-代码整理103~134【初级篇】
- POJ 2991 Crane 线段树+计算几何 出自“挑战程序设计竞赛”
- 挑战编程 程序设计竞赛训练手册-1.6.7 将军(Check_the_Check)
- 挑战编程 程序设计竞赛训练手册-1.6.2 扫雷(Minesweeper)
- 最小生成树---Kruskal算法---挑战程序设计竞赛读书笔记
- 系统性训练,励志刷完挑战程序设计竞赛-代码整理1~42【初级篇】