一、模拟------用最基本的方式思考问题。
简单模拟(模拟某个过程): 在试题中,已经详细给出了完成某一过程的步骤或规则,程序只须严格按照题意要求,模拟过程即可。
1、直叙式模拟(按照题意要求,直接模拟过程)
要忠实于原题,认真审题,千万不要疏漏任何条件,精心设计方便模拟的数据结构。
2、筛选法模拟(模拟过程中产生的所有可能的解,组成一个筛)
先从题意中找出约束条件,然后将筛中的每一个可能的解解放到约束条件的过滤器上,一次一次地将不符合条件的解过滤掉,最后在筛中的即为问题的解。
3、构造法模拟
需要完整精确地构造出反映问题本质的数学模型,并根据该模型设计表示状态变化的参数,计算模拟结果。
二、递归------程序调用自身的编程技巧称为递归。
是子程序在其定义或说明中直接或间接调用自身的一种方法。
在递归过程中,系统每一层的返回点局部变量等用栈来存储。
递归时,当前层的返回点和局部变量入栈;
回溯时,当前层的返回点和局部变量出栈;
如果递归过程无法到达递归边界或递归次数过多,则会造成栈溢出。
三、回溯------搜索算法中的一种控制策略。
搜索从初始状态出发,运用题目给出的条件,规则,按照纵深搜索的顺序递归扩展所有可能的情况,从中找出满足题意要求的解答。当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
回溯法解题的考虑因素:
1、定义状态:如何描述问题求解过程中每一步的状况。
2、边界条件:在什么情况下,程序不再递归下去。
3、搜索范围:若当前子状态不满足边界条件,则扩展子状态。
4、约束条件和最优性要求:扩展出的子状态应满足什么条件方可继续递归。
一般流程:
void run(当前状态) { if(当前状态为最佳目标状态) { 记下最优结果; return; } for(int i=算符最小值;i<=算符最大值;++i) { 算符i作用于当前状态,扩展出一个子状态; if(子状态满足约束条件&&子状态满足最优化性要求) return(子状态); } }
例:POJ1979 http://poj.org/problem?id=1979
递归过程为search(i,j),其中
1、状态:男子当前的位置(i,j),(i,j)为子程序的值参。递归调用search(row,col)后,便可得到男子经过的瓷砖数ans;
2、约束条件:若当前瓷砖在地图外,或不可访问,或已访问则回溯,否则设访问标记为true,经过的瓷砖数+1;
3、搜索范围为:(i,j)的相邻四个点,依次递归调用。
//第一次写,自己瞎写的丑陋AC代码= =
#include <cstdio> #include <iostream> #include <cstring> using namespace std; int ans=1,m,n; char map[22][22]; bool visited[22][22]; int dfs(int x,int y) { int i; map[x][y]='#'; visited[x][y]=false; ans++; if(x-1>=0&&visited[x-1][y]&&map[x-1][y]!='#') { dfs(x-1,y); } if(x+1<m&&visited[x+1][y]&&map[x+1][y]!='#') { dfs(x+1,y); } if(y-1>=0&&visited[x][y-1]&&map[x][y-1]!='#') { dfs(x,y-1); } if(y+1<n&&visited[x][y+1]&&map[x][y+1]!='#') { dfs(x,y+1); } return ans; } int main() { int i,j; //freopen("Atext.in","r",stdin); while(cin >> n >> m,m&&n) //这地方先输入的是列数n,后输入的是行数m,= = ,害我卡半天 { ans=0; memset(visited,true,sizeof(visited)); for(i=0;i<m;i++) for(j=0;j<n;j++) cin >> map[i][j] ; for(i=0;i<m;i++) { for(j=0;j<n;j++) { if(map[i][j]=='@') { dfs(i,j); cout << ans << endl; break; } } if(ans) break; } } return 0; }
四、贪心------Greedy Algorithm
贪心:在问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体上加以考虑,它所作出的仅仅是在某种意义上的局部最优解(是否是全局最优,需要证明)。
若要用贪心算法求解某问题的整体最优解,必须首先证明贪心的思想在该问题的应用结果就是最优解!
例一、事件序列问题(区间调度问题)hdu2037
例二、区间覆盖问题
例三、hdu1050
求解问题的基本步骤:
1、从问题的某个初始解出发。
2、采用循环语句,当可以向求解目标前进一步时,就根据局部最优策略,得到一个部分解,缩小问题的范围或规模。
3、将所有部分解综合起来,得到问题的最终解。
例:hdu1052田忌赛马(上海2004 区域赛)很多贪心的题像此题一样不是最朴素的贪心,而是需要作出一些变化。
这些都是ACM入门的基本算法(= =),个人总结,简单介绍。