程序=数据结构+算法+程序设计语言
项目=程序+项目管理+质量控制+代码规范
首先根据输入输出数据设计数据结构,再次设计相应的算法,最后使用某种编程语言。数据结构是算法实现的基础,算法依赖数据结构快速实现,二者相辅相承。
1.递推思想
顺推法
假设有一对兔子,兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。如果所有兔子都不死,那么一年以后可以繁殖多少对兔子?即斐波那契数列。
使用顺推法计算,F(n)=F(n-1)+F(n-2)。最后F(n-1)/F(n)=>0.618即黄金分割比。
逆推法
例如:假设父亲为小龙一次性存入大学的生活费,只容许其每个月初取出1000供下个月使用,考虑月利息为0.71%,且银行在每月最后一天才计算利息计入本金。问开学时应该存多少钱。思想:
第48个月,月初时,账户剩余F(48)=1000 第47个月,月初时,账户剩余F(47)=(1000/(1+0.0071))+1000=F(48)/(1+0.0071)+1000 第46个月,月初时,账户剩余F(46)=F(47)/(1+0.0071)+1000 ............
使用逆推,最终得到F(1)
2.穷举思想
满足2个条件:
⑴ 候选答案可数 ⑵ 候选答案有确定的集合(便于使用条件判断语句和循环语句)
例如:任意输入A,B,C,D,E,遍历加减乘除运算符,使“A( )B( )C( )D( )E =F”等式成立。思想:设定left,rigtht保存临时结果。left保存上次运算的结构,即下次运算的左侧,right保存下次将参加运算的数据,即下次运算的右侧内容。倘若为乘或除,直接运算right,倘若为加减,保存当前运算符,然后检查下一个运算符。若下一运算符为乘除,直接计算j结果保存至right,反之,计算left=left+flag*right。Operator[i]=0-4分别表示加减乘除。代码如下:
#include"iostream" using namespace std; void print(int num[5],int Operator[4],int answer)//输出某个解决方案 { int i=0; char index[4]={'+','-','*','/'}; cout<<num[0]; while(i<4) {cout<<index[Operator[i]];cout<<num[i+1];i++;} cout<<"="<<answer<<endl; } void findSolution(int num[5],int answer)//嵌套循环遍历所有的解决方案 { int count=0;//计算器 int flag;//1=+,-1=- int Operator[4]; double left,right;//切记此处一定定义为double //嵌套遍历 for(Operator[0]=0;Operator[0]<4;Operator[0]++) if(Operator[0]<3||num[1]!=0) //当为除的时候,运算数不为0 for(Operator[1]=0;Operator[1]<4;Operator[1]++) if(Operator[1]<3||num[2]!=0) for(Operator[2]=0;Operator[2]<4;Operator[2]++) if(Operator[2]<3||num[3]!=0) for(Operator[3]=0;Operator[3]<4;Operator[3]++) if(Operator[3]<3||num[4]!=0) {left=0;right=num[0];flag=1; for(int i=0;i<4;i++) { switch(Operator[i]) { case 0: left=left+flag*right;//运算的是当前符号之前2个数 right=num[i+1]; flag=1;//保存当前符号 break; case 1: left=left+flag*right;//运算的是当前符号的前2个数 right=num[i+1]; flag=-1;//保存当前符号,在后序操作有优先运算符的时候起作用 break; case 2: right=right*num[i+1]; break; case 3: right=right/num[i+1]; break; } } if(left+flag*right==answer) {count++;cout<<count<<":";print(num,Operator,answer);} } if(count) cout<<"总共有"<<count<<"种方案"<<endl; else cout<<"无有效解决方案,请重新输入"<<endl; } int _tmain(int argc, _TCHAR* argv[]) { cout<<"输入6个数,最后一个数为目标结果"<<endl; int num[5],answer; for(int i=0;i<5;i++) cin>>num[i]; cin>>answer; findSolution(num,answer); return 0; }
3.递归思想
使用递归算法应该注意:
⑴必须有明确的递归结束条件即递归出口 ⑵递归显得简洁但是运行效率低,主要反映在占存储空间 ⑶递归过程中次数过多可能导致栈溢出(系统将每一次递归调用的返回点、局部变量等存入系统的栈中,
例如:10进制转换为任意进制。
void Convert(vector<int> &ans,int number,int n) { if(!number) return; else {ans.push_back(number%n); Convert(ans,number/n,n);} }
4.分治思想
分治算法和递归算法常常是紧密结合使用的。将一个问题分成几个子问题,然后将几个子问题的答案合并起来就是上一级的答案。一般具有以下特征的问题,可用分治法解答:
⑴可以分解成若干小的问题 ⑵当分界到某种规模时,极易求解 ⑶合并子问题的解可以得到求解问题的解 ⑷子问题之间的解相互独立
例如:在《算法导论》第三版的40页提到这样的问题,重新解释该问题,即为求解某个一维矩阵中,相加值最大的子矩阵。这里使用分治法基于以下策略思考。谭若在矩阵中设立标号mid。那么这个子矩阵要么完全在mid之前或者之后,要么横跨mid,每一级分治中,都寻找这样的3个子数组然后比较大小进行决策。代码如下:
#include"iostream" #include"cmath" using namespace std; typedef struct submax {int low,high,maxsum;}SubArray; SubArray FindCrossingMax(int data[],int length,int low,int middle,int high)//寻找跨越middle的最大子数组 { int present_left_sum=data[middle],left_max=data[middle];//寻找最大左子数组 int left_label=middle; for(int j=middle-1;j>=low;j--) {present_left_sum+=data[j]; if(left_max<present_left_sum) {left_max=present_left_sum;left_label=j;}} int present_right_sum=data[middle+1],right_max=data[middle+1];//寻找最大右子数组 int right_label=middle+1; for(int j=middle+2;j<=high;j++) {present_right_sum+=data[j]; if(right_max<present_right_sum) {right_max=present_right_sum;right_label=j;}} SubArray max={left_label,right_label,left_max+right_max};//跨越最大子数组=左子数组(含mid)+最大右子数组(含mid+1) return max; } SubArray FindMaxSubArray(int data[],int length,int low,int high)//寻找最大子数组 { if(high==low) {SubArray max={low,high,data[low]};return max;} else {int mid=floor(((double)high+low)/2); SubArray leftmax=FindMaxSubArray(data,length,low,mid);//寻找最大左(相对middle)子数组 SubArray rightmax=FindMaxSubArray(data,length,mid+1,high);//寻找最大右(相对middle)子数组 SubArray crossingmax=FindCrossingMax(data,length,low,mid,high);//寻找跨越middle的最大子数组 if(leftmax.maxsum>=rightmax.maxsum&&leftmax.maxsum>=crossingmax.maxsum) return leftmax; else if(rightmax.maxsum>=leftmax.maxsum&&rightmax.maxsum>=crossingmax.maxsum) return rightmax; else return crossingmax; } } SubArray FindMaxSubArray(int data[],int length)//非递归法计算最大子数组 { int max=data[0]; int low=0,high=0;//初始化 for(int i=1;i<length;i++) {int present_sum=data[i],present_max=data[i]; int label=i; for(int j=i-1;j>-1;j--) {present_sum+=data[j]; if(present_max<present_sum) {present_max=present_sum;label=j;}} if(present_max>max) {max=present_max;low=label;high=i;} } SubArray maxarray={low,high,max}; return maxarray; } int _tmain(int argc, _TCHAR* argv[]) { int data[]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7}; SubArray max=FindMaxSubArray(data,sizeof(data)/sizeof(int),0,sizeof(data)/sizeof(int)-1);//分治法 SubArray max=FindMaxSubArray(data,sizeof(data)/sizeof(int));//非分治法 for(int i=max.low;i<=max.high;i++) cout<<data[i]<<" "; return 0; }
5.贪婪思想
解为局部最优解。但对大部分问题,该解基本上近似整体最优解。贪婪算法基于当前初始解,采取逐步逼近的策略,不需要回溯。该算法存在以下问题:
⑴不能保证解最优 ⑵不能用来求最大最小解问题 ⑶只能满足某些约束条件可行解的范围。
一般实现策略如下:
解初始化; while(尚未达到可行解总目标) 求可行解的一个解元素; 所有元素组成一个初始解;
6.试探思想
7.模拟思想