博主联系方式:
QQ:1540984562
微信:wxid_nz49532kbh9u22 QQ交流群:892023501

目录

  • 1、穷举法
  • 2、贪心算法
  • 3、递归与分治算法
  • 4、回溯算法
  • 5、数值概率算法

1、穷举法

基本思想:

在可能的解空间中穷举出每一种可能的解,并对每一个可能解进行判断,从中筛选出问题的答案。

关键步骤:划定问题的解空间,并在该解空间中一一枚举每一种可能的解。

注意点:

1、解空间的划定必须保证覆盖问题的全部解:只有问题的解集属于解空间集合才能使用穷举法求解。

2、解空间集合以及问题的解集一定是离散的集合。即可列的、有限的

例1:寻找给定区间的素数

分析:最简便的方法就是穷举法,在1-100中对每个整数进行判断。

code:

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include "malloc.h"
#include "conio.h"
int IsPrime(int n)
{//判断n是否为素数,如果是返回1,如果不是返回0int i;for (i=2;i<n;i++){if (n % i == 0) return 0;}return 1;
}
void GetPrime(int low, int high)
{//寻找【low,high】之间的素数int i;for (i=low;i<=high;i++){if (IsPrime(i)){printf("%d ",i);}}
}
//测试函数
int main()
{int low, high;printf("输入搜索范围:\n");printf("搜索起点:");scanf("%d",&low);printf("搜索终点:");scanf("%d", &high);printf("区域中的素数为:\n");GetPrime(low,high);_getche();return 0;
}

例2:TOM的借书方案:

TOM共有5本新书,要借给A、B、C三个同学,每人只能借1本书,则TOM可以有多少种借书方法?

分析:设5本书的序号为{1,2,3,4,5},每个同学借的书范围限定于此。方案总数不可能超过5 * 5 * 5=125种。由于1本书一次只能借给1位同学,因此在每一种借书方案中,元素有重复的排列组合一定不会是问题的解。所以可以描述如下:

//测试函数
int main()
{int i, j, k;int print_times = 0;printf("有如下的几种方案:\n");for(i=1;i<=5;i++)for(j=1;j<=5;j++)for(k=1;k<=5;k++)if (i != j && j != k && i != k){print_times++;printf(" (%d,%d,%d) ",i,j,k);     //输出借书方案if (print_times % 10 == 0) printf("\n");}_getche();return 0;
}

效果:

2、贪心算法

贪心算法不从整体最优上加以考虑,所做的仅是在某种意义上的局部最优解。而局部最优解叠加到一起便是该问题整体上的最优解,或者近似最优解。

严格意义上讲,要使用贪心算法求解问题,该问题应当具备如下性质:

1、贪心选择性质:指求解的问题的整体最优解可以通过一系列的局部最优解得到。所谓局部最优解,就是指在当前的状态下做出的最好的选择。

2、最优子结构性质:

一个问题的最优解包含着它的子问题的最优解

例题1:最优装船问题

有一批集装箱要装入一个载重量为C的货船中,每个集装箱的质量由用户自己输入指定,在货船的装载体积不限制的前提下,如何装载集装箱才能尽可能多地将集装箱装入货船中?

算法设计:

1、数组w[]存放每个集装箱的质量

2、数组x[]存放每个集装箱的取舍状态

3、变量c存放货船的剩余载重量,初始值为C

4、为了使装载可能多的集装箱,每次都选出数组w[]中当前w[i]值最小的集装箱,并将x[i]置1,同时c=c-w[i]

5、循环执行4操作,直道w[i]>c,表明货船装货已经达到最大载重量,输出x[]中所有x[i]=1的下标i

代码描述

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include "malloc.h"
#include "conio.h"void swap(int& a, int& b)
{//方法一:   int tmp = 0;tmp = b;b = a;a = tmp;//方法二:   //a = a+b;   //b = a-b;   //a = a -b;   //方法三:   //a ^= b ^= a ^= b;   //方法四:   //a = a+b-(b=a);
}
void sort(int w[], int t[], int n)
{int i, j;//动态开辟一个临时数组,存放w[]中的内容,用于排序int* w_tmp = (int*)malloc(sizeof(int) * n);for (i = 0;i < n;i++)t[i] = i;           //初始化t[]for (i = 0;i < n;i++)w_tmp[i] = w[i];    //复制数组w[]到w_tmp[]中//将w_tmp[]与t[]共同排序,当w_tmp实现从小到大排列后,t[]中存放的就是w_tmp[]元素在w[]中对应的下标for (i = 0;i < n - 1;i++)for (j = 0;j < n - i - 1;j++)if (w_tmp[j] > w_tmp[j + 1]){swap(w_tmp[j], w_tmp[j + 1]);swap(t[j], t[j + 1]);}
}
//输入: x[]集装箱取舍状态  w[]每个集装箱的质量 c船的剩余载重量 n一共几个货物
void Loading(int x[],int w[],int c,int n)
{int i, s = 0;//动态开辟出一个临时数组,存放w[]的下标,如果t[i],t[j],i<j,则有w[t[i]] <= w[t[j]];int* t = (int*)malloc(sizeof(int)*n);   sort(w, t, n);      //排序,用数组t[]存放w[]的下标for (i = 0;i < n;i++)x[i] = 0;           //初始化取舍状态数组for (i = 0;i < n && w[t[i]] <= c;i++){x[t[i]] = 1;c = c - w[t[i]];}
}
//t存放数组w[]的下标,使得如果i<j,则w[t[i]]<=w[t[j]];
//例如w[3]={5,3,2} 则t[3]={2,1,0},即将w[]从小到大排序,然后记录下w[i]的原本下标i在新的顺序中的位置。
//原本5,3,2三个元素排列顺序为:5,3,2,对应下标0,1,2,从小到大排之后5,3,2三个元素排列顺序为:2,3,5,下标对应原来的(2,1,0)int main()
{int x[5], w[5], c, i;printf("输入船最大载重量:\n");scanf("%d",&c);printf("输入5个载货箱的重量:\n");for (i = 0;i < 5;i++)scanf("%d",&w[i]);//进行最优装载Loading(x,w,c,5);printf("这些箱子将被装入:\n");for (i = 0;i < 5;i++){if (x[i] == 1) printf("BOX:%d ",i);}_getche();return 0;
}

3、递归与分治算法

基本思想:

将规模较大的问题分割为规模较小的同类问题,然后将这些子问题逐个加以解决,最终也就将整个大的问题解决了。

分治思想:举例,折半查找就是利用序列之间元素的顺序关系,不断缩小问题的规模,每次都将问题缩小为原来的1/2,采用顺序的方法查找需要O(n),而折半只需要O(long2n)

递归思想:一种直接或间接地调用原算法本身的一种算法。

设计递归算法需要注意的几点:

1、每个递归函数都必须有一个非递归定义得初始值,作为递归结束标志,或递归结束的出口

2、递归算法的运行较低,时间和空间复杂度都比较高,对于一些对时间和空间要求较高的程序,建议使用非递归算法设计。

例题:

1、计算整数的划分数

将一个正整数n表示为一系列的正整数之和:

n=n1+n2+n3+n4;(n1>=n2>=n3>=nk>=1,k>=1)

被称为正整数n的一次划分。一个正整数n可能存在不同的划分,例如正整数6的全部的划分为;

6=6

6=5+1

6=4+2、6=4+1+1

6=3+3、6=3+2+1、6=3+1+1+1

6=2+2+2、6=2+2+1+1、6=2+1+1+1+1

6=1+1+1+1+1+1

正整数n的不同的划分的个数被称为该正整数n的划分数。

编写一个程序,计算输入的正整数n的划分数

分析:设标记p(n,m)表示正整数n的所有不同的划分中,最大加数不大于m的划分个数。例如P(6,2)=4,因为在正整数6的全部划分中,最大加数不大于2的只有:6=2+2+2、6=2+2+1+1、6=2+1+1+1+1

6=1+1+1+1+1+1,四个划分。

建立如下4个递归关系:

1、P(n,1) =1.n>=1

任何正整数n的划分中,加数不大于1的划分有且仅有1种,即n=1+1+1+…+1(n个1)

2、P(n,m)=P(n,n),m>=n

最大加数等于n,不存在最大加数大于n的情况

3、P(n,n)=P(n,n-1)+1

最大加数为n的划分只有1种,即n=n,因此最大加数不大于n的划分数就是P(n,n-1)+1

4、P(n,m)=P(n,m-1)+P(n-m,m),n>m>1

正整数n的最大加数不大于m的划分数=n的最大加数不大于m-1的划分数+n的最大加数为m的划分数

例:

根据这四个式子书写code:

int P(int n, int m)
{if (m == 1 || n == 1) return 1;if (m > n) return P(n,n);if (m == n) return 1 + P(n, m - 1);return P(n, m - 1) + P(n - m, m);
}
int main()
{int n, s;printf("请输入一个整数\n");scanf("%d",&n);s = P(n, n);printf("整数的最大划分数为 %d \n",s);_getche();return 0;
}


2、递归折半查找

原本的折半查找code:

int bin_search(keytype key[], int n, keytype k)
{int low = 0, high = n - 1, mid;while (low <= high){mid = (low + high) / 2;if (key[mid] == k)return mid;if (k > key[mid])low = mid + 1;      //在后半序列中查找elsehigh = mid - 1;}return -1;                   //查找失败,返回-1
}

修改为递归形式:

typedef int keytype;
int bin_search(keytype key[], int low,int high,keytype k)
{int mid;if (low > high) return -1;else{mid = (low+high) / 2;if (key[mid] == k)return mid;else if (k > key[mid])return bin_search(key, mid + 1,high,k);elsereturn bin_search(key,low,mid-1,k);}
}int main()
{int n, i, addr;int A[10] = { 2,3,5,7,8,10,12,15,19,21 };printf("初始序列为:\n");for (i = 0;i < 10;i++)printf("%d ",A[i]);printf("\n输入待查找的元素:\n");scanf("%d",&n);addr = bin_search(A,0,9,n);if (addr != -1){printf("%d 下标为%d",n,addr);}else{printf("元素不在序列中");}_getche();return 0;
}

4、回溯算法

基本思想:

在包含问题的所有解的解空间中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去;如果该结点不包含问题的解,那就说明以该结点为根结点的子树一定不包含问题的最终解,因此要跳过对以该结点为根的子树的系统探索,逐层向其祖先结点回溯。这个过程叫做解空间树的“剪枝”操作。

如果应用回溯法求解问题的所有解,要回溯到解空间树的树根,这样才能保证根结点的所有子树都被探索到才结束。如果只要求得问题的一个解,那么在探索解空间树时,只要搜索到问题的一个解就可以结束了。

1、四皇后问题求解

问题描述:求解如何在一个NxN的棋盘上无冲突地摆放N个皇后棋子,在任意一个皇后所在位置的水平、数值、以及45度斜线上都不能出现其他皇后的棋子,否则视为冲突。

以四皇后问题为例,构建出一棵空间树,通过探索这棵解空间树,可以得到四皇后的一个解或者几个解。

根结点派生出4个子结点,每个结点都示意出前两个皇后可能摆放的位置。每个结点又可以派生出4个子结点,每个结点都示意出前3个皇后可能摆放的位置,整个解空间树为一棵4叉的满树,共包含4x4x4+4x4+4+1=85个结点.

应用回溯法解决问题:从根结点出发,深度优先搜索整个解空间树。当访问到根结点的第一个孩子时,观察结点,如果该结点不包含问题的解,那么由该结点作为根结点派生出来的子树中也肯定不会包含四皇后问题的解,停止向下搜索,回溯到根结点,继续太多根结点的下一个孩子结点。这就是”剪枝“操作,可以减少搜索的次数

在解决出四皇后问题时,并不一定要真的构建出这样的一棵解空间树,它完全可以通过一个递归回溯来模拟。所谓解空间树只是一个逻辑上的抽象。当然也可以用树结构真实地创建出一棵解空间树,不过比较浪费空间资源。

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include "malloc.h"
#include "conio.h"//四皇后问题求解
int count = 0;         //记录四皇后问题解的个数
//判断该位置的8邻域是否存在皇后,如果有返回0 如果没有返回1
int IsCorrect(int i,int j,int (*Q)[4])
{int s, t;for (s = i, t = 0;t < 4;t++)if (Q[s][t] == 1 && t != j) return 0;       //判断是否在同一行for (t = j, s = 0;s < 4;s++)if (Q[s][t] == 1 && s != i) return 0;       //判断是否在在同一列for (s = i - 1, t = j - 1;s >= 0 && t >= 0;s--, t--)if (Q[s][t] == 1) return 0;                  //判断是否在左上方for (s = i + 1, t = j + 1;s < 4 && t < 4;s++, t++)if (Q[s][t] == 1) return 0;                 //判断是否在右下方for (s = i - 1, t = j + 1;s >= 0 && t <4;s--, t++)if (Q[s][t] == 1) return 0;                   //判断是否在右上方for (s = i + 1, t = j - 1;s < 4  && t >=0;s++, t--)if (Q[s][t] == 1) return 0;                  //判断是否在左下方//否则返回1return 1;
}//输入参数:    j:二维数组Q的列号(0-3),最开始调用时,j=0,表明第一个皇后摆放在棋盘上的第一列。(*Q)[4]:指向二维数组每一行的指针
//中间变量:i:二维数组Q的行号(0-3),通过对变量i的4次循环,可以分别探索四皇后问题的4棵解空间树(以Q[0][0]、Q[1][0]、Q[2][0]、Q[3][0]为解空间树的根结点)
//当j=4时,表明Q第0-3列都已经放置好棋子了,说明此时得到了一个四皇后的解,因此程序返回上一层
void FourQueen(int j, int(*Q)[4])
{int i, k;//如果得到一个解if (j == 4){//打印出结果for (i = 0;i < 4;i++){for (k = 0;k < 4;k++)printf("%d ", Q[i][k]);printf("\n");}printf("\n");//解个数+1//返回到上一级_getche();count++;return;}//否则for (i = 0;i < 4;i++){if (IsCorrect(i, j, Q))      //如果Q[i][j]可以放置皇后,表明这一列不需要再探索了{Q[i][j] = 1;FourQueen(j+1,Q);       //探索下一列,递归深度优先搜索//Q[i][j]=0,以便循环,试探Q[i+1][j](下一行)是否可以摆放皇后,因为如果这一行如果没有找到解,需要清除这一行痕迹,然后到下一行继续探索Q[i][j] = 0;}}
}
//测试函数
int main()
{int Q[4][4];int i, j;//初始化数组Qfor (i = 0;i < 4;i++)for (j = 0;j < 4;j++)\Q[i][j] = 0;FourQueen(0,Q);           //执行四皇后求解printf("四皇后解法有%d种\n",count);_getche();
}

效果:

2、上楼梯问题

已知楼梯有20阶,上楼可以一步上1阶,也可以一步上2阶。编写一个程序计算共有多少种不同的上楼梯方法。

分析:采用递归回溯,用一棵空间二叉树描述

从根结点出发向下搜索,每经过一个结点,则表示这一步登上的台阶数。每次都将访问结点中的数字相加,记录为当前已经走过的台阶数,当这个数字等于20,就表示寻找出了一种上楼梯的方案。那么该结点以下的结点也就没必要再访问了,而是向其父结点回溯并继续探索下一分支。

代码:

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include "malloc.h"
#include "conio.h"#define MAX_STEPS 20        //定义20个台阶的楼梯
int Steps[MAX_STEPS] = { 0 };  //Steps[i]等于1或者等于2,记录第i步登上的台阶数
int cnt = 0;                   //记录上楼梯的方案的数目void Upstairs(int footstep,int count,int step)
{//footsetp:当前要登的台阶数、count:已经走过的阶数、step:走过的步数int i;if (count + footstep == MAX_STEPS){//得到一种上楼梯方案Steps[step] = footstep;cnt++;                 //方案数+1//打印出方案for (i = 0;i < step;i++){printf("%d ",Steps[i]);}printf("\n");//返回到父结点,不必再向下搜索}else if (count + footstep > MAX_STEPS){//超过了楼梯的阶数,不必再向下搜索,返回到父结点return;}else{//Steps[step] = footstep;                //记录当前上楼梯的台阶数step++;                              //步数+1count = count + footstep;          //记录目前已经走过的台阶数//递归探索后续的分支Upstairs(1,count,step);                //走1步的分支Upstairs(2, count, step);           //走2步的分支}
}
void UpstairsALL()
{Upstairs(1,0,0);           //从第一步上1个台阶开始探索解空间树Upstairs(2, 0, 0);           //从第一步上2个台阶开始探索解空间树
}int main()
{UpstairsALL();printf("一共有%d种解法\n",cnt);_getche();
}

效果:

5、数值概率算法

概率算法允许在执行过程中随机地选择下一步的计算步骤,因此使用概率算法有时会大大降低复杂度,提高算法的效率,但有时也可能得不到问题的全部答案。

概率算法可以分为4类:

1、数值概率算法

2、蒙特卡洛算法

3、拉斯维加斯算法

4、舍伍德算法

这里只介绍最基础的数值概率算法。

数值概率算法常用于解决数值计算的问题。应用数值概率算法往往只能得到问题的近似解,并且该近似解的精度一般随着计算时间的增加而不断提高。

例:f(x)=1-x^2;计算定积分:

该积分的几何意义如下:

如果随机地向图中虚线与x,y坐标轴所围成的正方形中投点,那么根据几何概率知识可知,随机点落入阴影区域的概率即为阴影部分的面积与虚线与x,y轴所围成的正方形的面积之比。

所以只要求出随机点落入阴影区域的概率的概率就求出了定积分的近似值。

算法描述:

#include<iostream>
#include <stdio.h>
#include <stdlib.h>
#include "malloc.h"
#include "conio.h"
#include "time.h"double Darts(int n)      //n为试验投点的个数,决定了计算概率的精度
{double x, y;time_t t;int i, count = 0;srand((unsigned)time(&t));for (i = 0;i < n;i++){//随机初始化投点的坐标x = rand() % 100 / 100.0;y = rand() % 100 / 100.0;if (y <= 1 - pow(x, 2))count++;     //如果随机点落入阴影区域,则用count++记录个数}return (double)count / (double)n;              //返回概率
}
//测试函数
int main()
{int n;char c;printf("请输入试验精度(越大精度越好):\n");scanf("%d", &n);printf("结果为:\n");printf("%f\n", Darts(n));_getche();return 0;
}

常用算法总结(穷举法、贪心算法、递归与分治算法、回溯算法、数值概率算法)相关推荐

  1. 数模算法-网格算法和穷举法

    网格算法和穷举法一样,只是网格法是连续问题的穷举. 比如要求在 N 个变量情况下的最优化问题,那么对这些变量可取的空间进行采点, 比如在 [ a; b ] 区间内取 M +1 个点,就是 a; a + ...

  2. 常见算法思想——穷举法

    常见算法思想--穷举算法 简单介绍 详细介绍 算法思路 算法特点 算法优化 实例演示 题目描述 题目分析 完整代码 简单介绍   在进行归纳推理时,如果逐个考察了某类事件的所有可能情况,因而得出一般结 ...

  3. 第1-6课:算法设计常用思想之穷举法

    这一课我们来介绍穷举法,有一些人把穷举法视为上不了台面的低级方法,这种想法是错误的,虽然穷举思想的原理简单,但是用穷举思想设计一个算法却一点也不简单.各种算法模式或思想没有高下之分,关键在于你是否能灵 ...

  4. 常用算法回顾——穷举法

    文章目录 一.穷举法 定义 算法思路 算法优缺点 示例: 一.穷举法 定义 穷举法是算法设计中经常使用的一种方法,基本思想是问题的要求将问题的所有可能的输入一一进行验证,看是否满足问题的条件,从而找到 ...

  5. 【算法】穷举法习题练习

    穷举法:在条件里确定答案的范围.遍历所有路径找到合适的结果 百马百担问题:有一百匹马,驮一百担货,大马驮3担,中马驮2担,两只小马驮一担,问有多少种方法,大中小马各几匹. 分析:从大马开始从0到100 ...

  6. 求最大公约数的4种方法C语言(辗转相除法、辗转相减法、穷举法、递归法)

    最大公约数,也称最大公因数.最大公因子,指两个或多个整数共有约数中最大的一个. 目录 问题描述 辗转相除法(欧几里得算法) 代码实现 辗转相减法 代码实现 暴力穷举法 代码实现 递归法 代码实现 测试 ...

  7. 算法设计之—直接 遍历/穷举法、贪心算法、动态规划、回溯法、EM方法

    算法是对完成特定问题的程序执行序列描述,表象为从问题初始状态到问题结束状态的所有路径之中寻找可行路径,若无先验经验,根据执行方式不同可以划分为无规则和有规则(启发式)方法. 无规则方法为穷举,改进方法 ...

  8. 【最大公约数 GCD】 --- 常用四大算法(辗转相除法,穷举法,更相减损法,Stein算法)

    [最大公约数 GCD] --- 常用的四大算法 1. 辗转相除法(又名欧几里德算法) 2. 穷举法(也称枚举法) 3. 更相减损法 (又名辗转相减法) 4. Stein算法 1. 辗转相除法(又名欧几 ...

  9. java 穷举法求水仙花数_常用算法-穷举法

    穷举法又称为枚举法,它是在计算机算法设计中用得最多的一种编程思想.它的实现方式是:在已知答案范围的情况下,依次地枚举该范围内所有的取值,并对每个取值进行考查,确定是否满足条件.经过循环遍历之后,筛选出 ...

最新文章

  1. Python超简单容易上手的画图工具库
  2. pybind 播放h264
  3. sklearn 岭回归
  4. cocos2d基础介绍
  5. 我是怎么保存公众号历史文章合集到本地的?当然是用python了!
  6. php显示网卡信息,netwox显示网络配置信息
  7. python绘制饼状图图例_使用matplotlib的所有饼图的通用图例
  8. [React] 尚硅谷 -- 学习笔记(一)
  9. linux用户名是什么_什么是Linux用户?
  10. 基于Qt的A*算法可视化分析
  11. 释放vmware磁盘空间
  12. 斐讯K2路由器刷固件实现校园网可使用
  13. stm8单片机程序加密方法 id加密技巧
  14. matlab K近邻均值平滑滤波,K近邻均值滤波
  15. Dynamic Web 2021Crack版,文档扫描和图像捕获
  16. 开发一个APP到底要多少钱?
  17. 经验分享 | ENVI app store
  18. 突然看到微信还有这种赞赏码
  19. 周博通 | 阿里开源首个 DL 框架、4000台服务器真实数据集;明年1月开源Blink
  20. [散文]茶想(作者:王莹莹)

热门文章

  1. ansi编码转换_8b/1b编码是个什么东东
  2. 移动端适配的几种解决办法
  3. 一键转发抢红包源码及搭建教程
  4. Cookie和会话Session
  5. vue inheritAttrs、$attrs和$listeners使用
  6. httpHandlers和httpModules接口介绍 (5)
  7. mpvue小程序以及微信直播踩坑总结
  8. 一步步构建大型网站架构 [转]
  9. 比较好的一些 ConcurrentHashMap讲解博客
  10. SD/MMC相关寄存器的介绍