计算机算法设计与分析

简介: 笔记

第2章 递归与分治策略


P11 例2-1阶乘函数

#include<stdio.h>
#include<stdlib.h>
int factorial(int n){
  if(n>1)
    return n*factorial(n-1);
  else
    return 1;
} 
int main(){
  printf("输入数:");
  int n=0;
  scanf("%d",&n);
  printf("阶乘为:%d",factorial(n));
}

P12 例2-2fibonacci函数

#include<stdio.h>
#include<stdlib.h>
int fibonacci(int n){
  if(n<=1)
  return 1;
  else
  return fibonacci(n-1)+fibonacci(n-2);
}
int main(){
  printf("输入数:");
  int n=0;
  scanf("%d",&n);
  printf("fibonacci数为:%d\n",fibonacci(n));
}

 

P12 例2-3Ackerman函数

#include<stdio.h>
#include<stdlib.h>
int Ackerman(int n,int m){
  if(n==1 && m==0)
    return 2;
  else if(n==0 && m>1) 
    return 1;
  else if(n>=2 && m==0)
    return n+2;
  else if(n>=1 && m>=1) 
    return Ackerman(Ackerman(n-1,m),m-1);
}
int main(){
  printf("输入两个数:");
  int n=0,m=0; 
  scanf("%d %d",&n,&m);
  printf("Ackerman函数为:%d",Ackerman(n,m));
} 


第3章 动态规划


P47 3.1矩阵连乘问题

void matrixMultiply(int **a,int **b,int **c,int ra,int ca,int rb,int cb){
  if(ca!=rb)//前面的A矩阵的行数不等于后面的B矩阵的列数 
    error("矩阵不可乘");
  for(int i=0;i<ra;i++){//i<行数ra
    for(int j=0;j<cb;j++){//j<列数cb 
      int sum=a[i][0]*b[0][j];//先求新数组的第一个元素 
      for(int k=1;k<ca;k++) 
        sum+=a[i][k]*b[k][j];//C矩阵的第i行第j列的元素等于A矩阵第i行中所有元素与B矩阵第j列中对应元素的乘积之和
      c[i][j]=sum;//将sum赋值给相乘后的数组C的元素 
    }
  }
}

P49 3.1.3计算最优值

//用动态规划算法解决,可依据其递推式以自底向上的方式进行计算
//在计算过程中,保存已解决的子问题答案,而在后面需要是只要简单查一下
//从而避免大量的重复计算,最终得到多项式时间的算法(时间复杂度小于等于n^2) 
void MatrixChain(int *p,int n,int **m,int **s){//输入参数存储在p数组,最优值数组m
                   //s为一个n*n的二维整型数组,表示断开的位置(分为两个矩阵的断点处) 
  for(int i=1;i<=n;i++)
    m[i][i]=0;//m为n*n的二维整型数组,表示每个子问题的最优值,即m[i][j]表示矩阵链Ai...j的最小乘次数。
  for(int r=2;r<=n;r++){
    for(int i=1;i<n-r+1;i++){
      int j=i+r-1;
      m[i][j]=m[i+1][j]+p[i-1]*p[i]*p[j];
      s[i][j]=i;
      //计算矩阵链m[i][j]的最小乘次数,同时记录断点位置s[i][j]
      for(int k=i+1;k<j;k++){//s[i][j]表示使得矩阵链Ai...j分为两个最小矩阵链之和,断开的位置k
      //当长度为k的左右两个矩阵链分别对应m[i][k]和m[k+1][j]时,计算得到当前断点下的乘运算次数t  
        int t=m[i][k]+m[k+1][j]+p[i-1]*p[k]*p[j];
        if(t<m[i][j]){//如果t比已知的最小值m[i][j]还小,则更新m[i][j](最优解)和s[i][j](最佳断点位置)的值
          s[i][j]=k;
        }
      }
    }
  }
} 

P50 3.1.4构造最优解

void Traceback(int i,int j,int **s){//按算法MatrixChain计算出的断点矩阵s指示的加括号方式输出计算A[i:j]的最佳计算次序 
  if(i==j)
    return;
  Traceback(i,s[i][j],s);
  Traceback(s[i][j]+1,j,s);
  cout<<"Multiply A "<<i<<","<<s[i][j];
  cout<<"and A"<<(s[i][j]+1)<<","<<j<<endl;
}//要输出A[1:n]的最优计算次序只要调用Traceback(1,n,s)即可 

P51 3.2.2重叠子问题

//在用递归算法自顶向下解决问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算
//动态规划算法对每个子问题只解一次,然后将其解保存在一个表格中
//当再次需要解该问题时,只是简单地用常数时间查看一下结果 
int RecurMatrixChain(int i,int j){
  if(i==j)//检查是否只有一个矩阵 
    return 0;
  int u=RecurMatrixChain(i,j)+p[i-1]*p[i]*p[j];//计算当前的矩阵链的最小乘法运算次数 
  s[i][j]=i;
  for(int k=i+1;k<j;k++){
    int t=RecurMatrixChain(i,k)+RecurMatrixChain(K+1,j)+p[i-1]*p[k]*p[j];
    if(t<u){
      u=t;
      s[i][j]=k;
    }
  }
  return u;//返回当前矩阵链的最小乘法运算次数u 
}

P53 3.2.3备忘录方法

//记忆化搜索优化的矩阵连乘动态规划算法 
int MemoizedMatrixChain(int n,int **m,int **s){//m记录子问题的最优值,s为最优解的矩阵链 
  for(int i=1;i<=n;i++){
    for(int j=i;i<=n;j++)
      m[i][j]=0;//对m[][]数组初始化 
    return LookupChain(1,n);//调用LookupChain函数,将1到n的矩阵链分解为子问题求解 
  }
} 
int LookupChain(int i,int j){
  if(m[i][j]>0)//判断是否已经求过子问题 
    return m[i][j];//如果是,则直接返回最优解 
  if(i==j)//只有一个矩阵 
    return 0;
  int u=LookupChain(i,i)+LookupChain(i+1,j)+p[i-1]*p[i]*p[j];
  s[i][j]=i;
  for(int k=i+1;k<j;k++){//枚举中间断点k,将其分解为i到k和k+1到j两个子问题 
    int u=Lookup(i,k)+LookupChain(k+1,j)+p[i-1]*p[k]*p[j];//p[]数组存储了子矩阵的维度 
    if(t<u){
      u=t;//记录最优值 
      s[i][j]=k;//记录最优值对应的断点 
    }
  }
  m[i][j]=u;
  return u;
}

P55 3.3.2计算最优值

//求解最长公共子序列(LCS)的动态规划算法 
void LCSLength(int m,int n,char *x,char *y,int **c,int **b){//m和n是两个字符串的长度
//c[][]是最长公共子序列长度,b[][]是最长公共子序列长度和两个字符串的匹配情况(1表示匹配,2表示取x字符,3表示取y字符)
  int i,j;
  for(i=1;i<=m;i++)
    c[i][0]=0;
  for(i=1;i<=n;i++)
    c[0][i]=0;
//将第0行和第0列全部赋值为0,因为长度为0的字符串没有公共子序列 
  for(i=1;i<=m;i++){
    for(k=1;j<=n;j++){
      if(x[i-1]==y[i-1]){
        c[i][j]=c[i-1][j-1]+1;
        b[i][j]=1;//最长公共子序列包含了这个字符 
      }//如果不相同,说明这两个字符不可能同时存在于最长公共子序列中,需要在x[0:i-2]和y[0:j-2]中寻找最长的公共子序列 
//这时,可以比较c[i-1][j]和c[i][j-1],将其较大值作为c[i][j]的值,并将对应的情况记录在b[i][j]中
      else if(c[i-1][j]>=c[i][j-1]){
        c[i][j]=c[i-1][j];
        b[i][j]=2;    
      }//如果是c[i-1][j]大,则说明最长公共子序列不包含x[i-1],因此b[i][j]为2,表示取x[i-1]字符 
      else{
        c[i][j]=c[i][j-1];
        b[i][j]=3;
      }//否则,最长公共子序列不包含y[j-1],b[i][j]为3,表示取y[j-1]字符 
    }
  }  
} 

P56 3.3.4构造最长公共子序列

//通过回溯记录的LCS最优路径,构造出最长公共子序列的函数 
void LCS(int i,int j,char *x,int **b){//i和j分别表示两个字符串的末尾位置 
  if(i==0||j==0)
    return;
  if(b[i][j]==1){//当前字符匹配,属于最长公共子序列的一部分 
    LCS(i-1,j-1,x,b);//递归到b[i-1][j-1],输出x[i-1],即当前匹配的字符
    cout<<x[i-1]; 
  }
  else if(b[i][j]==2)//说明当前字符没有匹配在公共子序列中,需要递归到b[i-1][j]
    LCS(i-1,j,x,b);//即在第一个字符串中向前找到一个字符,与第二个字符串中的当前字符匹配
  else//说明当前字符也没有匹配在公共子序列中,需要递归到b[i][j-1]
    LCS(i,j-1,x,b);//即在第二个字符串中向前找到一个字符,与第一个字符串中的当前字符匹配
} 

P56 3.4.3最大子段和问题的动态规划算法

int MaxSum(int n,int *a){//利用Kadane算法求解最大子段和问题
  int sum=0,b=0;//sum记录最大子段和 
  for(int i=1;i<=n;i++){
    if(b>0)
      b+=a[i];
    else
      b=a[i];
    if(b>sum)
      sum=b;
  }
  return sum;
} 

P57 3.4.1最大子段和问题的改进算法

int MaxSum(int n,int *a,int& besti,int& bestj){//Kadane算法,将时间复杂度降到O(n^2) 
  int sum=0;
  for(int i=1;i<=n;i++){
    int thissum=0;
//从第0个元素开始逐一遍历数组a,记录当前的最大子段和sum和当前的子段和thissum
    for(int j=i;j<=n;j++){
      thissum+=a[j];
      if(thissum>sum){//如果thissum比sum大
        sum=thissum;//更新sum
        besti=i;//记录对应的起点besti(即i)
        bestj=j;//记录终点bestj(即j)
      }
    }
  }
  return sum;
} 

P57 3.4.1最大子段和问题的简单算法

int MaxSum(int n,int *a,int& besti,int& bestj){//暴力枚举,用于求解数组a中的最大子段和,算法的时间复杂度为O(n^3) 
  int sum=0;
  for(int i=1;i<=n;i++){//遍历所有可能的起点i 
    for(int j=i;j<=n;j++){//便利所有可能的终点j 
      int thissum=0;
      for(int k=i;k<=j;k++)//遍历了i到j范围内所有元素,以求出i到j的子段和thissum
        thissum+=a[k];
      if(thissum>sum){//如果thissum大于目前已经求出的最大子段和sum
        sum=thissum;//更新sum 
        besti=j;
        bestj=j;
        //记录下对应的起点besti和终点bestj
      }
    }
  }
  return sum;
} 

P59 3.4.2最大子段和问题的分治算法

int MaxSubSum(int *a,int left,int right){//分治算法的实现求解最大子段和问题
  int sum=0;
  if(left==right)//左left和右边界right相等 
    sum=a[left]>0?a[left]:0;
  else{
    int center=(left+right)/2;//通过分治的方式不断缩小问题规模,最终求得区间[left,right]的最大子段和
    int leftsum=MaxSubSum(a,left,center);//左子段 
    int rightsum=MaxSubSum(a,center+1,right);//右子段 
    int s1=0;
    int lefts=0;
    for(int i=center;i>=left;i--){
      lefts+=a[i];
      if(lefts>s1)
      s1=lefts;
    }
    int s2=0;
    int rights=0;
    for(int i=center+1;i<=right;i++){
      rights+=a[i];
      if(rights>s2)
        s2=rights;
    }
    sum=s1+s2;
    if(sum<leftsum)
      sum=leftsum;
    if(sum<rightsum)
      sum=rightsum;
  }
  return sum;
}
int MaxSum(int n,int* a){//接受数组a的长度n和指向数组a的指针
  return MaxSubSum(a,1,n);//调用了MaxSubSum函数求解最大子段和问题。
}

P60 3.4.3最大子段和问题与动态规划算法的推广

int MaxSum2(int m,int n,int **a){//MaxSum2函数的总时间复杂度为O(n^3) 
  int sum=0;
  int *b=new int[n+1];//定义一维数组b,用于计算二维数组中从上到下某些行的元素和。
  for(int i=1;i<=m;i++){
    for(int k=1;k<=n;k++)
      b[k]=0;
    for(int j=i;j<=m;j++){
      for(int k=i;k<=n;k++)
        b[k]+=a[j][k];
      int max=MaxSum(n,b);//调用一个MaxSum函数求出b数组的最大子段和
      if(max>sum)
        sum=max;
    }
  }
  return sum;
} 

P70 3.8.2递归计算最优值

void MNS(int C[],int n,int **size){//二维数组size用来表示函数Size(i,j)的值 
  for(int j=0;i<C[1];j++)//第一上端点在[1][j]之前都是0 
    size[1][j]=0;
  for(int j=C[1];j<=n;j++)//第一上端点在[1][j]后都为1 
    size[1][j]=1;
  for(int i=2;i<n;i++){ 
    for(int j=0;j<C[i];j++)//j<C[i-1],出现交叉打架,不能选择当前线 
      size[i][j]=size[i-1][j];//size[i][j]值等于正上方的值 
    for(int j=C[i];j<=n;j++)//j>C[i-1],没有交叉了,可以选择当前线 
      size[i][j]=max(size[i-1][j],size[i-1][C[i]-1]+1);
//这时需要选择,是选择当前线还是不选择,选择标准是选择的线路数量要求最大 
//前为选用当前线,后为不选用 
  }
  size[n][n]=max(size[n-1][n],size[n-1][C[n]-1]+1);
  //数组的最后一个值,即为整个线路中可选择的线路的最大数量 
} 

P71 3.8.3构造最优解

void Traceback(int C[],int **size,int n,int Next[],int& m){
  int j=n;
  m=0;
  for(int i=n;i>1;i--){
    if(size[i][j]!=size[i-1][j]){
      Next[m++]=i;//上端点m向后进一 
      j=C[i]-1;//下端点j向前进一 
    }
    if(j>=C[1])
      Net[m++]=1;
  }
} 

第4章 贪心算法


P96 4.1活动安排问题

void GreedySelector(int n,int s[],int f[],bool A[]){
  A[1]=true;//用集合A来存储所选择的活动,A[1]为已选项 
  int j=1;
  for(int i=2;i<=n;i++){//从A[2]开始挑选活动 
    if(s[i]>=f[j]){//待选择活动的起始时间s[i],目前已选择的最后一个活动的结束时间f[j],作比较 
      j=i;//将活动i加入选择队列中 
    }
    else
      A[i]=false;//表示不选择活动i 
  }
} 

P99 4.1背包问题

void Knapsack(int n,float m,float v[],float w[],float x[]){//贪心算法解决背包问题(非0-1背包) 
  Sort(n,v,w);//Sort函数,排序各个物体的单位体积下的价值(价值v/质量w) 
  int i;
  for(i=1;i<=n;i++)//n为所有物品的总大小,用x[i]=0表示没被装入背包,x[i]=1表示被装入背包了 
    x[i]=0;
  float c=M;//c被赋初值为原始背包大小 
  for(i=1;i<=n;i++){
    if(w[i]>c)//背包满了 
      break;
    x[i]=1;//表示这个单位体积的物品被全部装入背包 
    c-=w[i];//现在背包的大小c要被减去装入物品占据的大小 
  }
  if(i<=n)
    x[i]=c/w[i];//x[i]表示第i个物品的所占体积在当前规定背包最大容量M的条件下被装入的比例
        //比如x[i]=0.5,表示第i个物品的一半被放入背包,另一半没有放入 
}
相关文章
|
1月前
|
机器学习/深度学习 人工智能 监控
AI算法分析,智慧城管AI智能识别系统源码
AI视频分析技术应用于智慧城管系统,通过监控摄像头实时识别违法行为,如违规摆摊、垃圾、违章停车等,实现非现场执法和预警。算法平台检测街面秩序(出店、游商、机动车、占道)和市容环境(垃圾、晾晒、垃圾桶、路面不洁、漂浮物、乱堆物料),助力及时处理问题,提升城市管理效率。
AI算法分析,智慧城管AI智能识别系统源码
|
1月前
|
算法
经典控制算法——PID算法原理分析及优化
这篇文章介绍了PID控制算法,这是一种广泛应用的控制策略,具有简单、鲁棒性强的特点。PID通过比例、积分和微分三个部分调整控制量,以减少系统误差。文章提到了在大学智能汽车竞赛中的应用,并详细解释了PID的基本原理和数学表达式。接着,讨论了数字PID的实现,包括位置式、增量式和步进式,以及它们各自的优缺点。最后,文章介绍了PID的优化方法,如积分饱和处理和微分项优化,以及串级PID在电机控制中的应用。整个内容旨在帮助读者理解PID控制的原理和实际运用。
93 1
C4.
|
1月前
|
存储 算法 C语言
关于c语言用计算机语言表示算法
关于c语言用计算机语言表示算法
C4.
17 1
|
1月前
|
算法 机器学习/深度学习 索引
【算法设计与分析】——搜索算法
【算法设计与分析】——搜索算法
40 1
|
1月前
|
算法 调度
【算法设计与分析】— —基础概念题(one)可作为日常联系或期末复习
【算法设计与分析】— —基础概念题(one)可作为日常联系或期末复习
47 1
|
1月前
|
算法 C语言 C++
嵌入式PID算法理论+实践分析
嵌入式PID算法理论+实践分析
26 0
|
2天前
|
机器学习/深度学习 数据采集 算法
共享单车需求量数据用CART决策树、随机森林以及XGBOOST算法登记分类及影响因素分析
共享单车需求量数据用CART决策树、随机森林以及XGBOOST算法登记分类及影响因素分析
|
3天前
|
移动开发 算法 数据可视化
数据分享|Spss Modeler关联规则Apriori模型、Carma算法分析超市顾客购买商品数据挖掘实例
数据分享|Spss Modeler关联规则Apriori模型、Carma算法分析超市顾客购买商品数据挖掘实例
|
4天前
|
算法 数据可视化 大数据
圆堆图circle packing算法可视化分析电商平台网红零食销量采集数据
圆堆图circle packing算法可视化分析电商平台网红零食销量采集数据
33 13
|
10天前
|
存储 分布式计算 负载均衡
分布式(计算机算法)
分布式(计算机算法)