可能有排版问题,可以到我的博客看看这篇文章
https://blog.ifycyu.ml/posts/8c2f6b20.html
前言
本文只针对客观题!!!
选择、填空、判断题自己看书!!!一定要看!
【代码是手打的,不保证正确】
第一章
1.2.1 时间复杂度
第二章
3.3.3 求第k小的元素
思路:按照快排的思想 第k小对应a[k-1]的值
对a[s…t]
- s>=t,说明a中元素个数<=1,如果s=t,k-1=s.这一个数就是第k小的数
- s<t,划分为a[s…i-1]和a[i+1…t],以i为基准划分
- k-1==i,a[i]为所求的值
- k-1<i,第k小元素在a[s…i-1]里,继续递归
- k-1>i,第k小元素在a[i+1…t]里,继续递归
#include<stdio.h>
int QucikSelect(int a[],int s,int t,int k)
{
int i=s,j=t;
int tmp;
if(s<t)
{
tmp = a[s];//选择第一个作为基准
while(i!=j)
{
while(j>i&&a[j]>=tmp)
j--;
a[i]=a[j];
while(i<j&&a[i]<=tmp)
i++;
a[j]=a[i];
}
a[i]=tmp;//类似快排的思想
if(k-1==i) return a[i];//对应2.1
else if(k-1<i) return QuickSelect(a,s,i-1,k);//对应2.2
else return QuickSelect(a,i+1,t,k);//对应2.3
}
else if(s==t&& s==k-1) //对应1
return a[k-1];
}
3.3.4 两个等长有序序列中位数
两个中位数比较,中位数较大的数组选小部分的,中位数较小的选大部分的
- 奇数,取剩下的
- 偶数:较大的数连同其剩下的,较小的数取剩下的(最后一次比较也是取较大数)
a = (11,13,15,17,19) b=(2,4,6,8,20)
15>6
a=(11,13) b=(8,20)
11>8
a=(11) b=(20)
11<20
20
3.4.1 最大连续子序列和问题
分治法求解,取中间位置 mid 有三种情况:
- 子序列在a[0…mid]
- 子序列在a[mid+1…n-1]
- 子序列在跨过mid的左右两边
a = {-2,11,-4,13,-5,-2} mid = (0+5)/2=2
分为 a[0…2] a[3…5]
左:11 右:13 中 20 max = 20
#include<stdio.h>
long max3(long a,long b,long c)
{
if(a<b) a=b;
if(a>c) return a;
else return c;
}
long maxSubSum(int a[],int left,int right)
{
int i,j;
long maxLeftSum,maxRightSum;
long maxLeftBorderSum,leftBorderSum;
long maxRightBorderSum,rightBorderSum;
if(left==right)
{
if(a[left]>0)
return a[left];
else return 0;
}
int mid =(left+right)/2;
maxLeftSum = maxSubSum(a,left,mid);//mid左边
maxRightSum = maxSubSum(a,mid+1,right);//mid右边
//跨过mid
maxLeftBorderSum=0,leftBorderSum=0;
for(i=mid;i>=left;i--)//从第mid个数开始,向左求最大子序列和
{
leftBorderSum+=a[i];
if(leftBorderSum>maxLeftBorderSum)
maxLeftBorderSum = leftBorderSum;
}
maxRightBorderSum=0,rightBorder=0;
for(j=mid+1;j<=right;j++)//从第mid+1个数开始,向右求最大子序列
{
rightBorderSum +=a[j];
if(rightBorderSum>maxRightBorderSum)
maxRightBorderSum=rightBorderSum
}
return max3(maxLeftSum,maxRightSum,maxLeftBorderSum+maxRightBorderSum);
}
第四章
4.2.4 最大连续子序列和
(-2, 11, -4, 13, -5, -2)
穷举法:算出每一个连续子序列的和,取最大值
三次循环
0 | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
-2 | 9 | 5 | 18 | 13 | 11 |
11 | 7 | 20 | 15 | 13 | |
-4 | 9 | 4 | 2 | ||
13 | 8 | 6 | |||
-5 | -7 | ||||
-2 |
int maxSubSum1(int a[],int n)
{
int i,j,k;
int maxSum,thisSum;
for(i=0;i<n;i++)
{
for (j=i;j<n;j++)
{
thisSum=0;
for(k=i;k<=j;k++)
thisSum+=a[k];
if(thisSum>maxSum)
maxSum=thisSum
}
}
}
两次循环(每一行的数字(第一个除外)可以由上一个数字加上a[j]计算)
int maxSubSum2(int a[],int n)
{
int i,j;
int maxSum=0,thisSum;
fot(i =0;i<n;i++)
{
thisSum=0;
fot(j=i;j<n;j++)
{
thisSum+=a[j]
if(thisSum>maxSum)
maxSum=thisSum
}
}
}
一次循环
循环如果相加值小于零,直接替换成0(加上负数肯定比较小)
int maxSubSum3(int a[],int n)
{
int i,maxSum=0,thisSum=0;
for(i=0;i<n;i++)
{
thisSum+=a[i];
if(thisSum<0)
thisSum=0;
if(maxSum<thisSum)
maxSum=thisSum
}
return maxSum;
}
4.2.5 求解幂集问题
-
穷举法:
a[0…2] = {1,2,3}
可以转化为二进制(1表示选中,0表示不选中)
000 001 010 011 … 111
对应为{},{3},{2},{2,3}…{1,2,3}
void inc(int b[],int n)二进制加法 { for(int i=0;i<n;i++)//0改成1,1改成0 { if(b[i])//1改成0,表示要进1位,继续循环 b[i]=0; else { b[i]=1; break;//0改成1,没有进位,退出循环 } } } void PSet(int a[],int b[],int n) { int i,k; int pw = (int)pow(2,n); for(i=0;i<pw;i++)//幂集有2^n个,因此要2^n次循环 { for(k=0;k<n;k++) if(b[k]) printf() inc(b,n); } }
-
增量穷举法
- {}
- 添加1->{1}
- 上面添加2->{2},{1,2}
- 上面添加3->{3},{1,3},{2,3},{1,2,3}
- 所有元素合起来就是所求解
void PSet(int n) { vector<vector<int>> ps1; vector<vector<int>>::iterator it; vector<int>s; ps.push_back(s); for(int i=1;i<=n;i++)//从1到n循环 { ps1=ps;//ps1放上一次循环的内容 for(it=ps1.begin();it!=ps1.end();++it) (*it).push_back(i);//往ps1添加 for(it=ps1.begin();it!=ps1.end();++it) ps.push_back(*it);//将ps1的元素添加到ps中 } }
4.2.6 0/1背包
采用求幂集的方法
void PSet(int n) { ... } void Knap(int w[],int v[],int W) { int count=0; int sumw,sumv; int maxi,maxsumw=0,maxsumv=0; vector<vector<int>>::iterator it; vector<int>::iterator sit; for(it=ps.begin();it!-ps.end();++it) { sumw=sumv=0; for(sit=(*it).begin();sit!=(*it).end();++sit)//由幂集选中选择哪个元素 { sumw+=w[*sit-1]; sumv+=v[*sit-1]; } if(sumw<=W)//能装入背包中 { if(sumv>maxsumv) { maxsumw=sumw; maxsumv=sumv; maxi=count; } } else ..;//不能装入背包 count ++; } }
-
4.2.7 全排列
- {1}
- 插入2-> {2,1} {1,2}
- 插入3->{3,2,1},{2,3,1},{2,1,3}, {3,1,2},{1,3,2},{1,2,3}
void Insert(vector<int> s,int i,vector<vector<int>> &ps1)
{
vector<int> s1;
vector<int>::iterator it;
for(int j=0;j<i;j++)
{
s1=s;
it=s1.begin()+j;
s1.insert(it,i);
ps1.push_back(s1);
}
}
void Perm(int n)
{
vector<vector<int>>ps1;
vector<vector<int>>::iterator it;
vector<int>s,s1;
s.push_back(1);
ps.push_back(s);
for(int i=2;i<=n;i++)
{
ps1.clear();
for(it=ps.begin();it!=ps.end();++it)
{
Insert(*it,i,ps1);
}
ps=ps1;
}
}
求解任务分配问题
按照全排列的思想,排列出每一种可能性,然后得出结果
将任务全排列
void Insert();
void Perm();
void Allocate(int n, int & mini ,int & minc)
{
Perm(n);
for(int i=0;i<ps.size();i++)//每一种方案
{
int cost = 0;
for(int j=0;j<ps[i].size();j++)//每一种方案的选择(j表示第j个人)
cost+=c[j][ps[i][j]-1]//ps[i][j]表示任务,由于从0开始,所以要-1
if(cost<minc)
{
minc=cost;
mini=i;
}}
}
第五章
5.1 回溯法
5.1.1 解空间
满足约束条件->可行解
最大或最小值->最优解
排列树,解空间树
5.1.2 回溯法
活结点->自身已生成但孩子结点没有全部生成
扩展结点->正在产生孩子结点的结点
死结点->所有子节点均已产生
剪枝函数 1. 用用约束函数在扩展结点处剪除不满足约束条件的路径 2. 用限界函数剪去得不到问题解或最优解的路径
回溯法步骤:
- 问题的解空间树至少包含问题的一个解或最优解
- 确定结点的扩展搜索规则
- 以DFS搜索解空间树,用剪枝函数避免无效搜索.DFS可以用回溯或迭代(非递归)回溯
5.1.3 回溯法框架
5.1.4 回溯法和DFS的异同
- 访问次序
- DFS目的是遍历,无序的,重要的是是否有访问过
- 回溯法目的是求解过程,本质是有序的
- 访问次数不同
- DFS 对访问过的顶点不再访问,仅访问一次
- 回溯法可能再次访问
- 剪枝不同
- DFS 不包含剪枝
- 很多回溯法用剪枝剪除不必要的分支
5.1.5 回溯法时间分析
假设解空间树共有m层,第1层有m0个满足约束条件的结点,每个结点有m1个满足约束条件,则第二层有m0m1个满足约束条件的结点…T(n) = m0+m0m1+…+m0m1…mn-1
通常子集问题:
时间复杂度: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ogpyxk90-1657519476460)(https://www.zhihu.com/equation?tex=O%282%5En%29)] 。因为每一个元素的状态无外乎取与不取,一共[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Be3rft6M-1657519476462)(https://www.zhihu.com/equation?tex=2%5En)] 种状态,,最终时间复杂度为 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sbLtdPJp-1657519476463)(https://www.zhihu.com/equation?tex=O%282%5En%29)] 。
排列问题分析:
时间复杂度:: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BIrDKtEd-1657519476464)(https://www.zhihu.com/equation?tex=O%28+n%21%29)] 。因为一共[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9FBguN00-1657519476465)(https://www.zhihu.com/equation?tex=n%21)] 种排列,最终时间复杂度为 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K5pIkZds-1657519476466)(https://www.zhihu.com/equation?tex=O%28+n%21%29)] 。
5.2 0/1背包
(左子树选,右子树不选)
重量和恰好为 W
左剪枝:当tw+w[ i ]超过W(背包满了)不再装
右剪枝: 当当前物品不选择时,即使选了后面所有物品,也无法达到W
不超过W
左剪枝方式不变
右剪枝:设置上界函数:bound(i) = tv+r (沿该方向物品价值上限),r表示剩余物品总价值
bound(i)<=maxv,说明右边全选了还不能比maxv大,剪枝
5.5 n后问题
每个皇后都要试探n列,共有n个皇后,解空间是子集树,每个结点有n棵子树,其时间复杂度就是 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AH7yq8m2-1657519476467)(https://www.zhihu.com/equation?tex=O%28n%5En%29)] 。或 因为 N 行N 列,皇后的排列方式共有 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m0hRMtp6-1657519476467)(https://www.zhihu.com/equation?tex=n%5En)] 种。
5.8 活动安排问题
相当于找某个排列,使得调度中兼容活动个数最大,对应解空间为排列树
解空间是一棵排列数,与全排列的时间复杂度相同,即复杂度为 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4vtON0rC-1657519476468)(https://www.zhihu.com/equation?tex=O%28+n%21%29)]
排列树和子集树的区别
子集树->选or不选
排列数->交换swap()
第六章 分支限界法
6.2 0/1 背包
6.2.1 队列式分支限界法
求最大值问题,对单位重量价值递减排列
考虑上界:
-
剩下所有物品都能装入,上界[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y6IvX6Qd-1657519476469)(https://www.zhihu.com/equation?tex=e.v%2B%5CSigma_%7Bj%3Di%2B1%7D%5Env%5Bj%5D&preview=true)]
-
不能放入,最多放到第k个,那么上界为[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mkLmNUEA-1657519476470)(https://www.zhihu.com/equation?tex=e.v%2B%5CSigma_%7Bj%3Di%2B1%7D%5Env%5Bj%5D&preview=true)]+(k+1能放入的重量)*(k+1的单位重量)
-
先检查左边结点是否符合条件,符合条件才进行左孩子的建立,进行入队(左边考虑的是不超重)
-
右孩子先建立结点,计算上界,再考虑是否入队(右边考虑的是重量别太小)
6.2.2 优先队列分支限界法
指定条件
bool operator<(const NodeType &s) const
{
return ub<s.ub;
}
重载运算符<
按照ub值越大越优先出队
题目
第七章 贪心算法
7.1.2 贪心算法性质
贪心选择性质:整体最优解可以通过一系列的局部最优选择来达到.即贪心法只在当前状态下做出最好的选择,然后再求解出这个选择后产生的相应子问题的解
即证明每一步的贪心选择最终导致整体的最优解->数学归纳法
最优子结构性质:一个问题的最优解包含其子问题的最优解->反证法证明
7.2 求解活动安排问题
对于本问题,所有活动按结束时间递增排序,就是要证明:若X是活动安排问题A的最优解,X=X’∪{1},则X’是A’={i∈A:ei≥b1}的活动安排问题的最优解。
当做出了对活动1的贪心选择后,原问题就变成了在活动2、…、n中找与活动1兼容的那些活动的子问题。亦即,如果X为原问题的一个最优解,则X’=X-{1}也是活动选择问题A’={i∈A |bi≥e1}的一个最优解。
首先证明总存在一个以活动1开始的最优解。
如果第一个选中的活动为k(k≠1),可以构造另一个最优解Y,Y中的活动是兼容的,Y与X的活动数相同。
那么用活动1取代活动k得到Y’,因为e1≤ek,所以Y’中的活动是兼容的,即Y’也是最优的,这就说明总存在一个以活动1开始的最优解。
反证法:如果能找到一个A’的含有比X’更多活动的解Y’,则将活动1加入Y’后就得到A的一个包含比X更多活动的解Y,这就与X是最优解的假设相矛盾
因此,在每一次贪心选择后,留下的是一个与原问题具有相同形式的最优化问题,即最优子结构性质。
思路:
1. 排序
2. X最优,证明取第一个最优
1. 取k≠1,构造一个另外的最优解Y
2. 用1替换k,得到Y'也是最优的
3. 在剩下的问题中找与1**兼容**的子问题的最优解
4. ( 第k个成立{1,2,3...k} U B,活动剩下S')
5. B有S'中的第一个活动,设为k+1 即 {1,2,3...k} U B = {1,2,3...k+1} U (B - (k+1) )
7.3 背包问题
排序
设Y最优
反证法即证明V(x)-V(y)>=0
ViXi=Wi*(Vi/Wi)Xi
找到一个Xi不为整数的值minj 分类讨论,得出**Vi/Wi **关系,Xi - Yi关系
- i<minj,Xi=1
- i>minj,Xi=0
- i=minj,Xi取部分
将V(x)-V(y)>=0展开计算
第八章 动态规划
8.2 整数拆分
问题 正整数m无序拆分成最大数为k的拆分方案个数
n=5,k=5
- 5=5
- 5=4+1
- 5=3+2
- 5=3+1+1
- 5=2+2+1
- 5=2+1+1+1
- 5=1+1+1+1+1
分析:
- n=1或k=1,f(n,k)=1 n=1只有1=1; k=1,只有n=1+1+…+1
- n<k,f(n,k)=f(n,n) 最大数为k,然而正整数最大为n=n,k的限制无意义
- n=k,f(n,n)=1+f(n,n-1) 先拆成n=n,一种方案,加上f(n,n-1)
- n>k,f(n,k)=f(n,k-1) + f(n-k,k)
- f(n,k-1) 拆分之后不含k,即最大拆分为k-1 (最大的情况为 n=(k-1)+…)
- f(n-k,k) 拆分之后右边含k,先拆个k出来 ,对剩下的继续拆分(n=k+…)
8.5 最长公共子序列
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z8cmzhHQ-1657519476471)(https://www.zhihu.com/equation?tex=A%3D(a_0%2Ca_1%2C…%2Ca_%7Bm-1%7D)]&preview=true) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XigFgIwY-1657519476472)(https://www.zhihu.com/equation?tex=B%3D(b_0%2Cb_1%2C…%2Cb_%7Bn-1%7D)]&preview=true) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jw6KCP2g-1657519476473)(https://www.zhihu.com/equation?tex=Z%3D(z_0%2Cz_1%2C…%2Cz_%7Bk-1%7D)]&preview=true)为A和B的最长子集
- a m − 1 a_{m-1} am−1 = b n − 1 b_{n-1} bn−1 则 z n − 1 z_{n-1} zn−1 = a m − 1 a_{m-1} am−1 = b n − 1 b_{n-1} bn−1 那么( z 0 z_{0} z0, z 1 z_{1} z1,…, z k − 2 z_{k-2} zk−2)是( a 0 a_{0} a0, a 1 a_{1} a1,…, a m − 2 a_{m-2} am−2)和( b 0 b_{0} b0, b 1 b_{1} b1,…, b n − 2 b_{n-2} bn−2)的一个最长公共子集
-
a
m
−
1
≠
b
n
−
1
a_{m-1} \not= b_{n-1}
am−1=bn−1
- z k − 1 ≠ a m − 1 z_{k-1} \not=a_{m-1} zk−1=am−1 ( z 0 z_{0} z0, z 1 z_{1} z1,…, z k − 1 z_{k-1} zk−1)是( a 0 a_{0} a0, a 1 a_{1} a1,…, a m − 2 a_{m-2} am−2)和( b 0 b_{0} b0, b 1 b_{1} b1,…, b n − 1 b_{n-1} bn−1)的一个最长公共子集
- z k − 1 ≠ b n − 1 z_{k-1} \not=b_{n-1} zk−1=bn−1 ( z 0 z_{0} z0, z 1 z_{1} z1,…, z k − 1 z_{k-1} zk−1)是( a 0 a_{0} a0, a 1 a_{1} a1,…, a m − 1 a_{m-1} am−1)和( b 0 b_{0} b0, b 1 b_{1} b1,…, b n − 2 b_{n-2} bn−2)的一个最长公共子集
$dp[i][j] $= 0 i=0或j=0,边界情况
d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j] = dp[i-1][j-1] +1 dp[i][j]=dp[i−1][j−1]+1 a [ i − 1 ] = b [ j − 1 ] a[i-1]=b[j-1] a[i−1]=b[j−1]
d p [ i ] [ j ] = M A X ( d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) dp[i][j] = MAX(dp[i][j-1],dp[i-1][j]) dp[i][j]=MAX(dp[i][j−1],dp[i−1][j]) a [ i − 1 ] ≠ b [ j − 1 ] a[i-1]\not=b[j-1] a[i−1]=b[j−1]
时间复杂度: O ( m n ) O(mn) O(mn)
8.8 0/1 背包
d p [ i ] [ r ] dp[i][r] dp[i][r] i表示第i个物品,r表示剩下容量
d p [ i ] [ 0 ] = 0 dp[i][0] = 0 dp[i][0]=0 边界条件 (剩下容量0)
d p [ 0 ] [ r ] = 0 dp[0][r] = 0 dp[0][r]=0 边界条件 (没有物品)
d p [ i ] [ r ] = d p [ i − 1 ] [ r ] dp[i][r] = dp[i-1][r] dp[i][r]=dp[i−1][r] r<w[i] 无法放下物品
$dp[i][r] = max(dp[i-1][r],dp[i-1][r-w[i]]+v[i]) $ 不选或者选 第i个物品
时间复杂度 O ( n W ) O(nW) O(nW)
8.12 滚动数组
8.12.1 滚动数组
每次操作仅保留若干个有用信息,新的元素不断循环刷新
一般用%来实现滚动
降低空间复杂度
8.12.2 滚动数组解决 0/1背包问题
d p [ i ] [ ∗ ] dp[i][*] dp[i][∗]只与 d p [ i − 1 ] [ ∗ ] dp[i-1][*] dp[i−1][∗] 有关
第十一章 计算复杂性理论
11.1.1 求解问题分类
多项式时间->易解
指数时间->难解
时间复杂度分3类
- 存在多项式
- 肯定不存在多项式
- 尚未找到,不能证明不存在
11.2 P类和NP类问题
已经找到的多项式时间界的计算机的计算机算法问题->P类问题
用确定性图灵机以多项式时间界可解的问题 ->P类问题
用非确定性图灵机以多项式时间界可解的问题 ->NP类问题
确定性图灵机是非确定性图灵机的特殊情况 P ⊆ N P P\subseteq NP P⊆NP
P->能在多项式时间求解
NP->给定正确
NP问题的代表问题之一是旅行商旅行(TSP)问题。
目前发现的NP问题还有很多,如布尔表达式的可满足性问题、图的顶点覆盖问题和背包问题等等
11.3 NPC问题
NPC(NP-completeness)的概念表明找到某个问题的有效算法至少和找NP中所有问题的有效算法一样难。
这里的有效性的含义是指为求解问题设计的算法的时间为多项式级的。
布尔表达式的可满足性问题是一个NPC问题
归纳起来,NP问题包含P问题和NPC问题,目前属于多项式时间界求解的问题都属P问题,NPC问题是属于NP问题中最难的问题,目前尚不能确定能否用多项式时间界算法来求解。
但已证明,如果NPC问题中有一个问题能用多项式时间界算法求解,则所有NPC问题都可用多项式时间界算法求解。