序:
本文参考《算法导论》,www.oi-wiki.com,信息学奥赛一本通(以下简称:信奥一本通),NOI 大纲(2023年修订版)
oj网站参考:洛谷:首页 - 洛谷 | 计算机科学教育新生态
信息学奥赛一本通:信息学奥赛一本通(C++版)在线评测系统
注:在读高中oier码字不易,每日完善部分内容,放心食用
一、算法复杂度
1.空间复杂度
2.时间复杂度
①.大Θ表示法
②.大O表示法
③.大Ω表示法
④.小o表示法
⑤.小ω表示法
⑥减少时间的小技巧
(1)输入/输出优化
(2)
二、入门算法
1.枚举
2.模拟
三、基础算法
1.高精度
①.加法
②.减法
③.乘法
④除法
2.数据排序
①.插入排序
(1)直接插入排序
(2)折半插入排序
②.希尔排序
②.选择排序
③.冒泡排序
④.归并排序
⑤.快速排序
⑥.基数排序
⑦.桶排序
⑧.计数排序
⑨.堆排序
3.递推
4.递归
5.搜索
①.广度优先搜索
②.宽度优先搜索
6.贪心
7.二分
四、字符串算法
五、动态规划(DP)
1.基本模型
①.先导:
(1)概念
动态规划是一种通过把原问题分解为 相对简单的子问题 的方式求解复杂问题的方法。
能用动态规划解决的问题,需要满足三个条件: 最优子结构,无后效性,子问题重叠。
无后效性:已经求解的子问题,不会再受到后续决策的影响。
子问题重叠:如果有大量的重叠子问题,我们可以用空间将这些子问题的解存储下来,避免重复求解相同的子问题,从而提升效率。
——引自www.oi-wiki
子问题重叠是提升算法效率的关键,dp数组下标的含义及其状态转移方程的书写是解题的关键。
——个人见解
(2)基本思路:
对于一个能用动态规划解决的问题,一般采用如下思路解决:
1.将原问题划分为若干 阶段,每个阶段对应若干个子问题,提取这些子问题的特征(称之为 状态);
2.寻找每一个状态的可能 决策,或者说是各状态间的相互转移方式(用数学的语言描述就是 状态转移方程;
3.按顺序求解每一个阶段的问题。
——引自www.oi-wiki
②.例题讲解
例1:(信奥一本通)
1258:【例9.2】数字金字塔
思路过程:
1.状态设计:dp[ i ][ j ]记录到达 i 行 j 列的最大值,即将[ 1 ][ 1 ]->[ R ][ R ]的问题转化为[ 1 ][ 1 ]->[ i ][ j ]的子问题;
2.考虑过程中,走到每一个节点a[ i ][ j ],都是由a[ i-1 ][ j-1 ]或者a[ i-1 ][ j ]走到的,那么我们取较大者,
那么状态转移方程:dp[ i ][ j ] += max( dp[ i-1 ][ j-1 ] , dp[ i-1 ][ j ] )
AC代码:(ans在i=R行的最大值)
#include<bits/stdc++.h>
using namespace std;
const int MAXR=1010;
int R,dp[MAXR][MAXR],ans=INT_MIN;
void in()
{
cin>>R;
for(int i=1;i<=R;i++)
for(int j=1;j<=i;j++)
cin>>dp[i][j];
}
void f()
{
for(int i=2;i<=R;i++)
for(int j=1;j<=i;j++)
dp[ i ][ j ] += max(dp[ i-1 ][ j-1 ] , dp[ i-1 ][ j ]);
for(int i=1;i<=R;i++)
ans=max(ans,dp[R][i]);
cout<<ans;
}
int main()
{
ios::sync_with_stdio(false);
in();
f();
return 0;
}
优化:
行数自下而上遍历,不必再取i=R行的max,ans在dp[ 1 ][ 1 ]
#include<bits/stdc++.h>
using namespace std;
const int MAXR=1010;
int R,dp[MAXR][MAXR],ans=INT_MIN;
void in()
{
cin>>R;
for(int i=1;i<=R;i++)
for(int j=1;j<=i;j++)
cin>>dp[i][j];
}
void f()
{
for(int i=R-1;i>=1;i--)
for(int j=1;j<=i;j++)
dp[ i ][ j ] += max(dp[ i+1 ][ j+1 ] , dp[ i+1 ][ j ]);
cout<<dp[1][1];
}
int main()
{
ios::sync_with_stdio(false);
in();
f();
return 0;
}
2.背包问题
①.01背包
②.完全背包
③.多重背包
④.其他
3.区间类dp
①先导
(1)概念
区间类动态规划是线性动态规划的扩展,它在分阶段地划分问题时,与阶段中元素出现的顺序和由前一阶段的哪些元素合并而来有很大的关系。
——引自www.oi-wiki
②例题讲解
例1:(信奥一本通)
思路过程:
1.考虑若本题数据排列不是环而是链,那么环可以是2n-1长度的链,第 i 堆石子可以向左边或者右边合并,dp[ i ][ j ]记录 [ i , j ]区间范围内的最大/最小值
2.将dp[ i ][ k ]与dp[ k+1][ j ]合并
4.树型dp
5.数位dp
数位dp适用问题:考虑整数区间 [ a , b] 之间满足一定条件的整数Z的个数;
思路过程:可以对于Z的每一数位进行搜索dfs,dfs(a,b,c,d,e)的返回值可以储存在数组dp[ a ][ b ][ c ][ d ][ e ]中,以实现记忆化搜索及数位dp
例1:(信奥一本通)
1585:【例 1】Amount of Degrees
思路过程:
B 的整数次幂 ——> B进制 ;
互不相等 ——> 每一数位上只有两种可能:0、1 ;(容易想到二叉树)
设计dp:第一维:i 表示搜索到第 i 位 ;
第二维:
6.状态压缩类dp
7.单调队列优化dp
8.斜率优化dp
9.换根dp
六、图论算法
1.基本图算法
①.图的定义
②.图的存储
(1) 直接存储边
(2)邻阶矩阵
非常朴素的思考方式,考虑图上边 Edge[ i ] 所连接的两个节点 U[ i ],V[ i ]及该边权值W i , 开辟二维数组G,G[ i ][ j ] 存储边 i --> j 的权值大小
有向图c++实例:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1000,MAXM=1000*999;
int n,m,G[MAXN][MAXN];
void in()
{
cin>>n>>m;
//n节点,m边;
for(int i=0;i<m;i++)
{
int u,v,w;
cin>>u>>v;
G[u][v]=G[v][u]=w;
}
}
int main()
{
ios::sync_with_stdio(false);
in();
return 0;
}
(3)邻阶表
(4)链式前向星
2.最小生成树
七、数据结构
八、数学
1.初等数论
2.离散和组合数学
3.线性代数
4.高等数学
5.概率论
6.博弈论
7. 最优化
8. 计算几何
9. 信息论