动态规划:
背包问题:
01背包 : 物品只能装一次
完全背包 : 物品可以多次装入
应用: 给定容量n,和m个物品,求是否可以装满,或者装满的最大价值
1. dp[j]=max(dp[j],dp[[j-c[i]]+w[i]); //前i个物品装入容量为j的背包中
dp[n] 表示用m个物品装满背包容量为n的最大价值
2. dp[j] += dp[j-w[i]]; // 装满背包容量为j, 有多少种方法
初始化 dp[i][0]=1
3. dp[j]= min(dp[j],dp[j-w[i]]+1); //装满背包,所需的最少物品个数
例:2022年国赛填空题
将2022拆分成10个互不相同的正整数之和,总共有多少种拆分方法?
这就是一道01背包的问题
线性dp:
最长公共子序列序列
a[i]==b[j] dp[i][j]=dp[i-1][j-1]+1; //相等时,+1
a[i]!=b[j] dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
最长上升子序列 dp[i]=max(dp[j])+1;
编辑距离
a[i]==b[j] dp[i][j]=dp[i-1][j-1];
a[i]!=b[j] dp[i][j]=min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1;
//替换 插入 删除
初始化: dp[i][0]=i dp[0][j]=j;
经典应用: 数字三角形
数论
模运算:
%
例: 2022年省赛 刷题统计
gcd最大公约数:
int gcd(int a,int b){ return b?gcd(b,a%b):a; }
应用 : 判断两个数是否互质:
lcm最小公倍数:
int lcm(int a,int b){
return a*b/gcd(a,b);
}
判断是否为素数:
bool is_prime(longlong n){
if(n<=1) return fase;
for(long lont i=2;i<=sqrt(n); i++){
if(n%i==0) return false;
return true;
}
基础数据结构:
并查集
初始化: f[i]=i; 指向自己
查找:
int find(int x){
if(x==f[x]) return x;
else return find(f[x]);
}
查找优化: 路径压缩
int find(int x){
if(x!=f[x]) f[x]=find(f[x]);
return f[x];
}
并查集的合并:
void merge(int x,int y){
x=find(x); y=find(y);
if(x!=y) f[x]=f[y];
}
并查集的应用: kruskal最小生成树 、 tarjan最近公共祖先
树上问题
最近公共祖先:
类似的问题描述: 有n个点,n-1条路,求u,v最少花费多少
LCA模板:
vector<int> G[N]; //用vector存图,方便也好操作
int dep[N]; // 保存结点深度
int fa[N][21]; //结点v向上跳2^i个结点,是那个点
void dfs(int u,int father){
dep[u]=dep[father]+1;
fa[u][0]=father;
for(int i=1;i<=20;i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
} // 给点u向上跳,赋值
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(v!=father){
dfs(v,u);
} //递归孩子
}
int lca(int a,int b){
if(dep[a]<dep[b]) swap(a,b); //这一步,确保是a点往上跳
for(int i=20;i>=0;i--){
if(dep[fa[u][i]]>=dep[b]) a=fa[u][i];
} //a往上跳
if(a==b) return b; //判断b是不是最近公共祖先
for(int i=20;i>=0;i--){
if(fa[a][i]!=fa[b][i]){
a=fa[a][i]; b=fa[b][i];
}
} //点a,b一块跳
return fa[a][0]; //最后肯定是落在最近公共祖先的孩子上
//所以跳1下
}
tarjan离线算法模板
结合并查集
int fa[N]; //并查集,一定要初始化 fa[i]=i,指向自己
int vis[N]; //是否访问
vector<int> G[N];
vector<PII> query[N]; //存查询
int ans[N]; //存查询结果
int find(int x){
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}
void tarjan(int u){
vis[u]=1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
if(!vis[v]){
tarjan(v);
fa[v]=u; //回到u,更新v的父亲
}
for(int i=0;i<query[u].size();i++){
int v=query[u][i].first; int k=query[u][i].second;
if(vis[v])
ans[k]=find(v);
}
图论:
存图
1. 邻接矩阵 g[N][N];
2. 邻接表 vector<int> g[N];
这两个存图是很好理解的,写算法的话也够用。
图的应用
1. 最短路问题
dijk算法:
求任一点到其余各点的最短路,它还可以使用堆优化
STL中的priority_queue是不错的选择。
floyd算法: 最简单的,原理就是用一个点作为中转,去更新其他点
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
这个模板很好记住,而且它求得是任意两点最短路径。
所以当你比赛时,其他的算法模板忘记了,这个就是最好的选择了
2. 最小生成树
prim算法
kruskal算法
3. 拓扑排序
使用队列来实现算法,入队条件
有向图 degree[i] == 0
无向图 degree[i] == 1
能力有限,只会这么些,最后祝大家2024蓝桥杯取得好成绩