算法设计思想

程序=数据结构+算法+程序设计语言

项目=程序+项目管理+质量控制+代码规范

    首先根据输入输出数据设计数据结构,再次设计相应的算法,最后使用某种编程语言。数据结构是算法实现的基础,算法依赖数据结构快速实现,二者相辅相承。

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.模拟思想


原文地址:https://www.cnblogs.com/engineerLF/p/5393120.html