计算机算法设计

计算机算法设计

A、递归和分治策略、回溯法

B、贪心算法、分支限界法、动态规划

C、算法设计的数据结构运用

(1)分治法(divide and conquer method)

将原问题划分成若干个规模较小而结构与原问题相似的子问题,递归地解决这些子问题,然后再合其结果,就得到原问题的解。

特征:

该问题的规模缩小到一定的程度就很容易解决
该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质
利用该问题分解出的子问题的解可以合并为该问题的解;
该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题

(2)动态规划(dynamic programing method)

与分治法相似,也是通过组合子问题的解而解决整个问题。区别是,动态规划适用于分解得到的子问题往往不是相互独立的(重叠的子问题)。在这种情况下如果采用分治法,有些子问题会被重复计算多次,动态规划通过记录已解决的子问题,可以避免重复计算。

要素:最优子结构、重叠子问题

(3)贪心法(greedy method)

贪心在解决问题的策略上目光短浅,只根据当前已有的信息就做出选择,而且一旦做出了选择,不管将来有什么结果,这个选择都不会改变。换言之,贪心法并不是从整体最优考虑,它所做出的选择只是在某种意义上的局部最优。这种局部最优选择并不总能获得整体最优解(Optimal Solution),但通常能获得近似最优解(Near-Optimal Solution)。

要素:贪心选择性质、最优子结构性质

动态规划的核心,先计算子问题,再由子问题计算父问题

贪心选择性质:一个全局最优解可以通过局部最(贪心)选择来达到。

1)分治法和动态规划法的区别
共同点:二者都要求原问题具有最优子结构性质,都将原问题分成若干个子问题,然后将子问题的解合并,形成原问 题的解。

不同点:动态规划法是将待求解问题分解成若干个相互重叠的子问题,而分治法是分解成若干个互不相交的子问题。利用分治法求解,这些子问题的重叠部分被重复计算多次。而动态规划法将每个子问题只求解一次并讲其保存在一个表格中,当需要再次求解此子问题时,只是简单地通过查表获得该子问题的解,从而避免了大量的重复计算。

动态规划适用于分解得到的子问题往往不是相互独立的。在这种情况下如果采用分治法,有些子问题会被重复计算多次,动态规划通过记录已解决的子问题,可以避免重复计算

2)动态规划法和贪心法的区别
共同点:

贪心算法和动态规划算法都要求问题具有最优子结构性质。

不同点:

动态规划算法中,每步所做的选择往往依赖于相关子问题的解,因而只有在解出相关子问题时才能做出选择。

而贪心算法,仅在当前状态下做出最好选择,即局部最优选择,然后再去解做出这个选择后产生的相应的子问题。

贪心无法解决动态规划的问题,但是动态规划能解决贪心的问题。虽然能够应用贪心算法一定能够应用动态规划法,但是一般来说,贪心算法的效率高于动态规划法,因而还是应用贪心算法。

动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每做一次贪心选择就将所求问题简化为规模更小的子问题。

1、动态规划

https://blog.csdn.net/u013309870/article/details/75193592
https://www.cnblogs.com/raichen/p/5772056.html

记住求解的方式有两种:①自顶向下的备忘录法自底向上。

1.1 原理

①最优子结构

用动态规划求解最优化问题的第一步就是刻画最优解的结构,如果一个问题的解结构包含其子问题的最优解,就称此问题具有最优子结构性质。因此,某个问题是否适合应用动态规划算法,它是否具有最优子结构性质是一个很好的线索。使用动态规划算法时,用子问题的最优解来构造原问题的最优解。因此必须考查最优解中用到的所有子问题。

②重叠子问题

在斐波拉契数列和钢条切割结构图中,可以看到大量的重叠子问题,比如说在求fib(6)的时候,fib(2)被调用了5次,在求cut(4)的时候cut(0)被调用了4次。如果使用递归算法的时候会反复的求解相同的子问题,不停的调用函数,而不是生成新的子问题。如果递归算法反复求解相同的子问题,就称为具有重叠子问题(overlapping subproblems)性质。在动态规划算法中使用数组来保存子问题的解,这样子问题多次求解的时候可以直接查表不用调用函数递归。

①自顶向下的备忘录法
问题模型:斐波那契数列
Fibonacci (n) = 1;   n = 0
Fibonacci (n) = 1;   n = 1
Fibonacci (n) = Fibonacci(n-1) + Fibonacci(n-2)
 
//算法思想:自上而下求解,如果子问题已经求解过,则直接返回
//否则,保存子问题的解
public static int Fibonacci(int n)
{
    if(n <=0 ) return n;
    int[] arrayMemo= new int[n+1];//0~n有n+1个元素
    for(int i = 0; i < n; i++)
        arrayMemo[i] = -1;
    
    return fib(n,arrayMemo);
}

public static int fib(int n,int []Memo)
{
    if(Memo[n] != -1) return Memo[n];//如果已经计算过则不重复计算
   
    if(n < = 2) Memo[n] = 1;
    else Memo[n] = fib(n-1,Memo) + fib(n-2,Memo);
    
    return Memo[n]
}
②自底向上的动态规划
问题模型:斐波那契数列
Fibonacci (n) = 1;   n = 0
Fibonacci (n) = 1;   n = 1
Fibonacci (n) = Fibonacci(n-1) + Fibonacci(n-2)
    
//算法思想 先计算子问题,再由子问题计算父问题
public static int fib(int n)
{
   if(n <= 0) return n;
    int[] arrayMemo= new int[n+1];//0~n有n+1个元素
    arrayMemo[0] = 0;
    arrayMemo[1] = 1;
    
    for(int i = 2; i <=n; i++)
    {
        arrayMemo[i] = arrayMemo[i-1] + arrayMemo[i-2];
    }
    
    return arrayMemo[n];
}

//自底向上方法也是利用数组保存了先计算的值,为后面的调用服务。
//观察参与循环的只有 i,i-1 , i-2三项,
// 因此该方法的空间可以进一步的压缩如下:
public static int fib(int n)
{
   if(n <= 0) return n;
    
    int Memo_i_1 = 1;//Fibonacci(n-1) -- 前一个元素
    int Memo_i_2 = 0;//Fibonacci(n-2) -- 前两个元素
    int res;
    
    for(int i = 2; i <=n; i++)
    {
        res = Memo_i_1 + Memo_i_2;
        Memo_i_2 = Memo_i_1;
        Memo_i_1 = res;
    }
    
    return arrayMemo[n];
}

1.1 钢条问题

//长度n 收益r 第一段长度i,r = r(i)+r(n-i)
//钢条分成两段 i和n-1计算收益
//方法一:递归实现
int cut(int []r,int n)
{
    //r(i) 第i段的收益
    //钢条长度
    if(n == 0) return 0;
    int  money = 0;
    //分为i 和 n-i段,计算收益
    //如:10米长(1,9) 、(2,8)...
    for(int i = 1, i  < n;i++)
    {
        money = max(r[i]+cut(r,n-i),money);
    }
    return moeny;
}

//方法一:备忘录版本
int memo(int []r,int n)
{
    int Memo[n+1];
    for(inti = 0;i < n;i++)
    {
        memo[i] = -1;
    }
    return cun(r,n,memo);
}
int cut(int []r,int n,int[] Memo)
{
    int money = -1;
    if(Memo[n] != -1)return Memo[n];
    if(n == 0) returnmoney = 0;
    else
    {
        for(int i = 1; i < n;i++)
        {
            moeny = max(money,r[i]+cut(r,n-i,Memo));
        }
        Memo[n] = money;
        return money;
    }
}

//3、动态规划
int buttom_up_cut(int []r,int n)
{
    int Memo[n+1];
    //求外层循环,分别计算分为两段后,不同策略下的解
    for(int i=1; i < n;i++)
    {
        int money = -1;
        //求i的最优解
        for(j = 1; j < i;j++)
        {
            money = max(money,r[j-1]+Memo[i-j]);
        }
        r[i] = money;
    }
    
    return r[n];
}

程序设计基础

1、基本数据类型、各种运算符和表达式、基本控制结构

2、数组的定义、数组元素的引用、数组的初始化,掌握和字符串相关的库函数

3、函数的定义语法,函数调用的参数的传递机制;局部和全局变量的有效范围

4、结构体类型变量的定义、引用、初始化方法,结构体数组的定义、初始化和应用,共同体变量的定义和使用方法

5、地址和指针的基本概念,如何使用指针来处理数组、字符串以及结构体,函数指针的基本概念以及使用

6、文件的定义以及对文件进行各种操作的库函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值