分治算法求出的子问题是互相独立的;
贪心算法不追求最优解,只追求可行解,因此不具备最优子结构的特性;
动态规划算法追求最优子结构性质和重叠子问题性质;
回溯算法把问题解空间划分为图或者树,采用深度优先搜索策略遍历,遍历的过程中寻找可行解和最优解;
分支界限类似于回溯算法,它以广度优先遍历搜索解空间树。
1、回溯法
N皇后问题
1、按照行来摆放皇后,如果某一行摆放不下皇后时,那么就回溯,返回上一行重新摆放皇后的位置; 2、软考时只需要表示列,不需要表示行; 3、两个皇后处于斜线:两个皇后的行号和列号相差相同的值;
#include <stdio.h>
#define N 4;//皇后的个数
int q[N+1];//每一行存放皇后的列标
int check(int j){//检查第j个皇后的下标是否合法
for(int i=1;i< j;i++){
if(q[i]==q[j] || abs(i-j)==abs(q[i]-q[j]))
return 0;
}
return 1;
}
//非递归
void queue(){
for(int i=1;i<=N;i++){//初始时还没有放置
q[i]=0;
}
int answer=0;
int j=1;//第几个皇后
while(j>=1){
q[j]=q[j]+1;//皇后向后摆放一位
while(q[j]<=N && check(j)==0){//判断第q[j]列位置是否合法
q[j]=q[j]+1;//第q[j]列位置不合法,向后移动一位
}
if(q[j]<=N){//没有到达或者刚好到达边界
if(j==N){//最后一个皇后选好位置了
answer ++;
for(int k=0;k<N;k++)printf("%d",q[k]);//输出结果
}else{//还没有到最后一位皇后
j++;
}
}else{//超出列的最大值,说明这一行没有合法的位置
q[j]=0;//将这一行归零
j--;//回溯到上一行
}
}
}
//递归
int answer;
void queue(int j){
int i;
for(i=1;i<=N;i++){
q[j]=i;//保证皇后可以取到一行的所有位置,一个一个遍历,挨个确定是否满足条件
if(check(j)){//摆放位置合法
if(j==N){//已经摆到了最后一个皇后
answer++;
}else{
queue(j+1);//摆放下一个皇后
}
}
}
}
2、分治法(归并排序,快速排序,分硬币)
把难以解决的大问题分解成规模较小的小问题
最大字段和问题
1、分解,将序列分解成两段S1+S2 有三种情况:
(1)最优解在S1中,S2=0;
(2)最优解在S2中,S1=0;
(3)最优解在S1+S2中,最终结果是S1+S2
2、合并,比较1中的三种情况的最大值为最终值
时间复杂度:O(nlogn)
int MaxSubSum(int *Array,int left,int right){
int sum=0;
if(left==right){//分解到单个整数,不继续分解,跳出递归的条件
if(Array[left]<0)
sum=0;
else
sum=Array[left];
}else{//从中间划分子序列
int center=(left+right)/2;
int leftsum=MaxSubSum(Array,left,center);
int rightsum=MaxSubSum(Array,center+1,right);
//求左边序列的最小和
int s1=0;
int lefts=0;
for(int i=center;i>=left;i++){
lefts=lefts+Array[i];
if(lefts>s1)s1=lefts;
}
//求右边序列的最小和
int s2=0;
int rights=0;
for(int j=center+1;j<=right;j++){
rights=rights+Array[j];
if(rights>s2)s2=rights;
}
sum=s1+s2;
if(leftsum>sum)sum=leftsum;
if(rightsum>sum)sum=rightsum;
return sum;
}
}
3、动态规划(0-1背包问题,公共最长子序列)
求解最优性质的问题,最优值,最优解,存储所有子问题的解
0-1背包问题
时间复杂度:O(NW)
空间复杂度:O(NW)
分别求子问题的解:W=0-4,N=0-5的子问题的最优解,列一个表格保存
dp[i][j]:[0,i]物品任取放入容量为j的背包中的最大价值
1、不放物品i(0):dp[i-1][j]
2、放物品i(1):前提:背包容量j大于等于物体i的重量
dp[i-1][j-weight[i]]+value[i]**------>1**
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])
初始化:dp[0][j] 和 dp[i][0] 都要初始化
其他i,j下标为非0值的时候,dp[i][j]可以初始化成任意值
#define N 4 //物品数量
#define W 5 //背包容量
int v[]={0,2,4,5,6};//物品的价值
int w[]={0,1,2,3,4};//物品的重量
int dp[N+1][W+1]={};
for(int i=1;i<N;i++){
for(int j=1;j<W;j++){
if(j>=w[i]){//取第i个物体
temp=dp[i-1][j-w[i]])+v[i];
}
dp[i][j]=max(dp[i-1][j],temp);
}
}
return dp[N][W];//最大值,最优解
矩阵连乘
动态规划
时间复杂度:O(n3)
空间复杂度:O(n2)
分析A: M1*M2:计算次数=20*5*35,计算结果矩阵大小M12:20*35
M12*M3:计算次数=20*35*4 计算结果矩阵大小M123:20*4
M123*M4:计算次数=20*4*25 计算结果矩阵大小M1234:20*25
最终的计算次数是20*5*35+20*35*4+20*4*25
以此类推计算BCD,得到最小值为最优解
先把矩阵维数较大的相乘,消掉大维数的矩阵
公共最长子序列
时间复杂度是O(n2)
4、贪心算法
做出的选择是局部最优,不能保证获得全局最优,得到近似最优解;
最优子结构:可以使用动态规划和贪心算法,重叠子问题选择动态规划,贪心选择选择贪心法(全局的最优解可以通过局部最优解得到);
部分背包问题-最大单位重量价值先放进背包的原则
时间复杂度是O(nlogn)