程序=数据结构+算法+程序设计语言
项目=程序+项目管理+质量控制+代码规范
首先根据输入输出数据设计数据结构,再次设计相应的算法,最后使用某种编程语言。数据结构是算法实现的基础,算法依赖数据结构快速实现,二者相辅相承。
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.模拟思想