各种常见算法 & 数据结构模板

1. 最长不下降子序列(LIS)

1.1 \(O(n^2)\) 做法

点击查看代码
for (int i = 1;i <= n;i++) {
cin >> a[i];
dp[i] = 1;
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j < i;j++) {
if (a[i] > a[j]) {
dp[i] = max(dp[i],dp[j] + 1);
}
}
ans = max(ans,dp[i]);
}

1.2 \(O(n \log n)\) 做法

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
int dp[N], n, x, k = 1;
int main() {
cin >> n >> x;
dp[1] = x;
for (int i = 2; i <= n; i++) {
scanf("%d",&x);
if (x > dp[k]) {
dp[++k] = x;
} else {
int p = lower_bound(dp + 1, dp + k + 1, x) - dp;
dp[p] = x;
}
}
cout << k;
return 0;
}

2. 背包问题

2.1 01背包-二维数组

点击查看代码
	cin >> maxw >> n;
for (int i = 1;i <= n;i++) {
cin >> w >> v;
for (int j = 1;j <= maxw;j++) {
if (w > j) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = max(dp[i - 1][j],dp[i - 1][j - w] + v);
}
}
}
cout << dp[n][maxw];

2.2 01背包-一维数组优化

点击查看代码
	cin >> maxv >> n; // maxv: 价值
for (int i = 1;i <= n;i++) {
cin >> v;
for (int j = maxv;j >= v;j--) {
dp[j] = max(dp[j],dp[j - v] + v);
}
}
cout << dp[maxv];

2.3 完全背包-一维数组优化

点击查看代码
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 1e4 + 10, S = 1e7 + 10;
ll dp[S], vi, wi, maxw, n;
int main() {
scanf("%lld%lld", &maxw, &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &wi, &vi);
for (int j = wi; j <= maxw; j++) {
dp[j] = max(dp[j], dp[j - wi] + vi);
}
}
printf("%lld", dp[maxw]);
return 0;
}

2.4 多重背包-普通版本

点击查看代码
#include <iostream>

using namespace std;

const int N = 110;
int dp[N], v, s, w;
int n, maxw;
int main() {
scanf("%d%d", &n, &maxw);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &w, &v, &s);
for (int k = 1; k <= s; k++) {
for (int j = maxw; j >= w; j--) {
dp[j] = max(dp[j], dp[j - w] + v);
}
}
}
printf("%d", dp[maxw]);
return 0;
}

2.5 多重背包-二进制优化版

点击查看代码
#include <iostream>

using namespace std;

const int N = 2e4 + 10;
int dp[2010], v[N], w[N];
int n, wi, vi, si, maxw, k;
int main() {
scanf("%d%d", &n, &maxw);
for (int i = 1; i <= n; i++) {
scanf("%d%d%d", &wi, &vi, &si);
for (int t = 1; t <= si; t *= 2) {
k++;
v[k] = t * vi;
w[k] = t * wi;
si -= t;
}
if (si > 0) {
k++;
v[k] = si * vi;
w[k] = si * wi;
}
}
for (int i = 1; i <= k; i++) {
for (int j = maxw; j >= w[i]; j--) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
printf("%d", dp[maxw]);
return 0;
}

2.6 分组背包

点击查看代码
#include <iostream>

using namespace std;

const int N = 110;
int dp[N], v[N][N], w[N][N], s[N];
int n, maxw;
int main() {
scanf("%d%d", &n, &maxw);
for (int i = 1; i <= n; i++) {
scanf("%d", &s[i]);
for (int j = 1; j <= s[i]; j++) {
scanf("%d%d", &w[i][j], &v[i][j]);
}
}
for (int i = 1; i <= n; i++) {
for (int j = maxw; j >= 1; j--) {
for (int k = 1; k <= s[i]; k++) {
if (w[i][k] <= j)
dp[j] = max(dp[j], dp[j - w[i][k]] + v[i][k]); }
}
}
printf("%d", dp[maxw]);
return 0;
}

2.7 二维费用背包

点击查看代码
#include <iostream>

using namespace std;

const int N = 410;
int dp[N][N], maxw, maxv, vi, wi, c;
int n;
int main() {
scanf("%d%d", &maxw, &maxv);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> wi >> vi >> c;
for (int j = maxw; j >= wi; j--) {
for (int k = maxv; k >= vi; k--) {
dp[j][k] = max(dp[j][k], dp[j - wi][k - vi] + c);
}
}
}
cout << dp[maxw][maxv]; return 0;
}

3. 图论最短路

3.1 朴素Dijkstra

时间复杂度:\(O(n^2)\)

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 60;
int a[N][N];
int d[N];
bool f[N];
int n,s;
int main() {
cin >> n >> s;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
cin >> a[i][j];
}
}
memset(d,0x3f,sizeof(d)); // 初始化为INF
d[s] = 0; // 走到起点的最短路为0
for (int i = 1;i <= n;i++) {
int mi = -1;
// 找到最小数下标
for (int j = 1;j <= n;j++) {
if (!f[j] && (mi == -1 || d[j] < d[mi])) {
mi = j;
}
}
f[mi] = true;
// 从最短路的点开始更新最短路
for (int j = 1;j <= n;j++) {
if (!f[j] && a[mi][j] && a[mi][j] + d[mi] < d[j]) {
d[j] = a[mi][j] + d[mi];
}
} }
for (int i = 1;i <= n;i++) {
if (i != s) {
//走不到 -> 0x3f3f3f3f
if (d[i] == 0x3f3f3f3f) cout << -1 << " ";
else cout << d[i] << " ";
}
}
return 0;
}

3.2 优先队列BFS优化 Dijkstra

时间复杂度:\(O(m \times logm)\)

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
typedef pair<int,int> PII;
struct node{
int to,next,len;
}a[N];
int pre[N],k = 0;
int n,m,x,y,len;
priority_queue<PII,vector<PII>,greater<PII>> q; // 优先队列
void add(int x,int y,int len) { // 建边
a[++k] = {y,pre[x],len};
pre[x] = k;
}
bool f[N];
int d[N];
int main() {
scanf("%d%d",&n,&m);
for (int i = 1;i <= m;i++) {
scanf("%d%d%d",&x,&y,&len);
add(x,y,len);
}
// 初始化
q.push({0,1});
memset(d,0x3f,sizeof(d));
d[1] = 0; while (!q.empty()) {
PII h = q.top();
int dis = h.first,p = h.second;
q.pop();
if (f[p]) continue;
f[p] = true;
if (p == n) { // 走到终点
cout << dis;
return 0;
}
for (int i = pre[p];i;i = a[i].next) { // 更新最短路
int to = a[i].to;
if (a[i].len + dis < d[to]) {
d[to] = a[i].len + dis;
q.push({a[i].len + dis,to});
}
}
}
cout << -1;
return 0;
}

3.3 Floyd

时间复杂度:\(O(n^3)\)

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 110,INF = 0x3f3f3f3f;
int d[N][N];
int n,m,x,y,l;
int main() {
cin >> n >> m;
memset(d,0x3f,sizeof(d));
for (int i = 1;i <= n;i++) d[i][i] = 0; for (int i = 1;i <= m;i++) {
cin >> x >> y >> l;
d[x][y] = l;
}
for (int k = 1;k <= n;k++) {
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
if (d[i][k] != INF && d[k][j] != INF) {
d[i][j] = min(d[i][j],d[i][k] + d[k][j]);
}
}
}
}
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
if (d[i][j] == INF) cout << "N ";
else cout << d[i][j] << " ";
}
cout << endl;
}
return 0;
}

4.树

4.1 树的直径

两次深搜求直径:

  1. 从树上任意找一个点 \(x\) 搜索离 \(x\) 最远的点 \(u\) ,\(u\) 一定是直径的一个端点;

  2. 从直径的一个端点 \(u\) 开始搜索,搜索出离 \(u\) 最远的点 \(v\) ,\(v\) 一定是直径的另一个端点,因此 \(uv\) 一定是直径。

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 110;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N];
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
} void dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
dfs(a[i].to,x);
}
}
} int main() {
cin >> n;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
int x = 1;
// 求出离1号点最远的那个点
for (int i = 2;i <= n;i++) {
if (dep[i] > dep[x]) x = i;
}
dfs(x,0);
int y = 1;
// 找出离x号点最远的点y
for (int i = 1;i <= n;i++) {
if (dep[i] > dep[y]) y = i;
}
printf("%d",dep[y] - 1);

4.2 树的中心

树的中心指的是,该结点离树中的其他结点,最远距离最近

树的中心满足如下性质:

(1)它一定在某条树的直径上,且到直径的两端的距离差不超过1

(2)即使树有多条直径,但树的中心至多有2个,且均为直径的中点。

求树的中心:由于树的中心一定在直径上,求出直径的两个端点 \(u v\),加深 \(v\) 深度更深。

(1) 如果 \(dep[v]\) 是奇数,爬树 \(dep[v]/2\) 次;

(2) 如果 \(dep[v]\) 是偶数,爬树 \(dep[v]/2-1\) 次,得到两个中心的靠下的一个点,另一个点就是该点的父;

点击查看代码
#include <iostream>

using namespace std;

const int N = 1e5 + 10;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N],fa[N];
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
} void dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
fa[x] = fath;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
dfs(a[i].to,x);
}
}
} int main() {
cin >> n;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
int x = 1;
for (int i = 2;i <= n;i++) {
if (dep[i] > dep[x]) x = i;
}
dfs(x,0);
int y = 1;
for (int i = 1;i <= n;i++) {
if (dep[i] > dep[y]) y = i;
}
if (dep[y] % 2 == 1) {
int d = dep[y] / 2;
for (int i = 1;i <= d;i++) {
y = fa[y];
}
printf("%d",y);
} else {
int d = dep[y] / 2 - 1;
for (int i = 1;i <= d;i++) {
y = fa[y];
}
if (y < fa[y]) cout << y << " " << fa[y];
else cout << fa[y] << " " << y;
}
return 0;
}

4.3 树的公共祖先(LCA)

解法一:染色法

点击查看代码
#include <iostream>

using namespace std;

const int N = 1e5 + 10;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N],fa[N];
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
}
bool vis[N]; void dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
fa[x] = fath;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
dfs(a[i].to,x);
}
}
} int main() {
cin >> n;
int u,v;
cin >> u >> v;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
vis[u] = true;
while (fa[u] != 0) {
u = fa[u];
vis[u] = true;
}
while (!vis[v]) {
v = fa[v];
}
cout << v;
return 0;
}

解法二:两个结点同时向上移动

思路:

(1)求出每个结点的深度;

(2)如果两个结点重叠,那么得知公共祖先;

(3)如果不重叠,选择深度更大的结点,向父元素移动,重复上述过程,直到重叠。

点击查看代码
#include <iostream>

using namespace std;

const int N = 1e5 + 10;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N],fa[N];
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
}
bool vis[N]; void dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
fa[x] = fath;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
dfs(a[i].to,x);
}
}
} int main() {
cin >> n;
int u,v;
cin >> u >> v;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
while (u != v) {
if (dep[u] > dep[v]) {
u = fa[u];
} else {
v = fa[v];
}
}
cout << u; return 0;

4.4 树的重心

求出每个结点删除后的最大子树的大小,并求出该值的最小值,最后循环看哪些树删除后子树大小==所求的最小值,哪些结点就是重心。

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
int n,x,y;
struct node{
int to,next;
}a[N * 2];
int pre[N];
int k = 0;
int dep[N],fa[N],si[N];
int ma[N];
int mi = INT_MAX;
void add(int x,int y) {
a[++k] = {y,pre[x]};
pre[x] = k;
}
bool vis[N]; int dfs(int x,int fath) {
dep[x] = dep[fath] + 1;
fa[x] = fath;
si[x] = 1;
for (int i = pre[x];i;i = a[i].next) {
if (a[i].to != fath) {
si[x] += dfs(a[i].to,x);
}
}
return si[x];
} int main() {
cin >> n;
for (int i = 1;i <= n - 1;i++) {
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs(1,0);
for (int i = 1;i <= n;i++) {
ma[i] = n - si[i];
for (int j = pre[i];j;j = a[j].next) {
if (a[j].to != fa[i]) {
ma[i] = max(ma[i],si[a[j].to]);
}
}
mi = min(ma[i],mi);
}
for (int i = 1;i <= n;i++) {
if (ma[i] == mi) {
cout << i << " ";
}
}
return 0;
}

5.最长公共子序列(LCS)

5.1 \(O(n^2)\) 做法

点击查看代码
#include <iostream>

using namespace std;

const int N = 1010;
int n, dp[N][N], a[N], b[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (a[i] == b[j]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
printf("%d", dp[n][n]);
return 0;
}

5.2 \(O(n \log n)\) 做法

点击查看代码
#include <iostream>

using namespace std;

const int N = 1e5 + 10;
int dp[N], a[N], b[N], n, c[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
c[a[i]] = i;
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
dp[1] = c[b[1]];
int k = 1;
for (int i = 2; i <= n; i++) {
if (c[b[i]] > dp[k]) {
dp[++k] = c[b[i]];
} else {
int p = lower_bound(dp + 1, dp + k + 1, c[b[i]]) - dp;
dp[p] = c[b[i]];
}
}
printf("%d", k);
return 0;
}

6. 并查集

6.1 朴素并查集

点击查看代码
int find(int x) {
if (x == fa[x]) return x;
return find(fa[x]);
}
点击查看代码
void merge(int x, int y) {
int rx = find(x), ry = find(y);
if (rx != ry) fa[rx] = ry;
}

6.2 路径压缩

点击查看代码
int find(int x) {
if (x == fa[x]) return x;
return fa[x] = find(fa[x]);
}

7. 最小生成树

7.1 \(Kustral\) 算法

点击查看代码
for (int i = 1;i <= k;i++) {
if (find(a[i].x) != find(a[i].y)) {
merge(a[i].x, a[i].y);
sum += a[i].len;
c++; if (c == n - 1) {
printf("%d", sum);
return 0;
}
}
}

8. 素数筛法

8.1 埃氏筛法

点击查看代码
#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;
bool f[N];
int n, ans;
int main() {
scanf("%d", &n);
f[0] = f[1] = true;
for (int i = 2;i <= sqrt(n);i++) {
if (!f[i]) {
for (int j = i * 2;j <= n;j += i) {
if (!f[j]) {
f[j] = true;
}
}
}
}
for (int i = 1; i <= n; i++) {
if (!f[i]) ans++;
}
printf("%d", ans);
return 0;
}

8.2 欧拉筛法

点击查看代码
f[0] = f[1] = true;
for (int i = 2;i <= e;i++) {
if (!f[i]) primes[++k] = i;
for (int j = 1;i * primes[j] <= e;j++) {
f[i * primes[j]] = true;
if (i % primes[j] == 0) break;
}
}

C++常见算法&数据结构模版的更多相关文章

  1. acm常见算法及例题

    转自:http://blog.csdn.net/hengjie2009/article/details/7540135 acm常见算法及例题  初期:一.基本算法:     (1)枚举. (poj17 ...

  2. 经典面试题(二)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯

    1.正整数序列Q中的每个元素都至少能被正整数a和b中的一个整除,现给定a和b,需要计算出Q中的前几项, 例如,当a=3,b=5,N=6时,序列为3,5,6,9,10,12 (1).设计一个函数void ...

  3. 算法数据结构 | 只要30行代码,实现快速匹配字符串的KMP算法

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法数据结构专题的第29篇文章,我们来聊一个新的字符串匹配算法--KMP. KMP这个名字不是视频播放器,更不是看毛片,它其实是由Kn ...

  4. Hadoop学习笔记—12.MapReduce中的常见算法

    一.MapReduce中有哪些常见算法 (1)经典之王:单词计数 这个是MapReduce的经典案例,经典的不能再经典了! (2)数据去重 "数据去重"主要是为了掌握和利用并行化思 ...

  5. [Machine Learning] 机器学习常见算法分类汇总

    声明:本篇博文根据http://www.ctocio.com/hotnews/15919.html整理,原作者张萌,尊重原创. 机器学习无疑是当前数据分析领域的一个热点内容.很多人在平时的工作中都或多 ...

  6. 常见算法是js实现汇总(转载)

    常见算法是js实现汇总 /*去重*/ <script> function delRepeat(arr){ var newArray=new Array(); var len=arr.len ...

  7. paper 12:机器学习常见算法分类汇总

    机器学习无疑是当前数据分析领域的一个热点内容.很多人在平时的工作中都或多或少会用到机器学习的算法.这里南君先生为您总结一下常见的机器学习算法,以供您在工作和学习中参考. 机器学习的算法很多.很多时候困 ...

  8. AI - 机器学习常见算法简介(Common Algorithms)

    机器学习常见算法简介 - 原文链接:http://usblogs.pwc.com/emerging-technology/machine-learning-methods-infographic/ 应 ...

  9. 前端常见算法的JS实现

    1.冒泡排序 function bubbleSort(arr){ var i = 0, j = 0; for(i=1; i<arr.length; i++){ for(j=0; j<=ar ...

  10. Java 中常见的数据结构

    1.数据结构有什么作用? 当使用 Java 里面的容器类时,你有没有想过,怎么 ArrayList 就像一个无限扩充的数组,也好像链表之类的.很好使用,这就是数据结构的用处,只不过你在不知不觉中使用了 ...

随机推荐

  1. 安装Hadoop单节点伪分布式集群

    目录 安装Hadoop单节点伪分布式集群 系统准备 开启SSH 安装JDK 安装Hadoop 下载 准备启动 伪分布式模式安装 配置 配饰SSH免密登录本机 测试启动 单节点安装YARN 伪分布式集群 ...

  2. Kerberos、黄金票据与白银票据

    kerberos Kerberos是一个网络认证协议,用于验证用户和服务之间的身份,解决分布式计算环境中的身份验证问题.它使用加密技术来提供安全的身份验证,并防止网络中的身份欺骗攻击.Kerberos ...

  3. java发送http请求(jquery发送http请求,前后端看这一篇文章够了,很完整)

    为什么写这篇博客? 1.目前很多系统使用了微服务架构,那么各个微服务之间进行内部通信一般采用http协议的方式,springcloud中提供了ribbon,feign,openFeign等组件. 但是 ...

  4. 用 Hugging Face 推理端点部署 LLM

    开源的 LLM,如 Falcon.(Open-)LLaMA.X-Gen.StarCoder 或 RedPajama,近几个月来取得了长足的进展,能够在某些用例中与闭源模型如 ChatGPT 或 GPT ...

  5. 升讯威在线客服系统是如何实现对 IE8 完全完美支持的(怎样从 WebSocket 降级到 Http)【干货】

    简介 升讯威在线客服与营销系统是基于 .net core / WPF 开发的一款在线客服软件,宗旨是: 开放.开源.共享.努力打造 .net 社区的一款优秀开源产品. 完整私有化包下载地址 https ...

  6. 【算法】单调栈 & 单调队列学习笔记

    1. 单调栈简介 1.1 前言 今天是 2023/1/15,一中寒假集训阶段性的结束了.集训的学习笔记可以在本人 blogs 的[算法]标签栏中找. 马上就要过年了,提前祝大家新年快乐! 1.2 什么 ...

  7. 【go语言】2.3.1 错误处理的基本概念

    在 Go 语言中,错误处理是通过返回错误值进行的,而不是像一些其他语言那样通过抛出和捕获异常.Go 语言有一个内置的接口类型 error,专门用于处理错误. error 接口 error 是一个内置的 ...

  8. VScode 中golang 调试 F5,json文件内容更改 Go

    调试:在Vscode软件中调试.go文件代码 1.1.1 确保调试文件正常运行 准备好.go文件,并且该文件能正常运行,终端输入命令运行程序,如:go run time.go 测试代码: packag ...

  9. 快速解决 const 与 typedef 类型组合时 ,const修饰谁的问题

    C++使用typedef 给复合类型定义别名时,与const结合会产生看似"令人困惑"的类型推定,例如 typedef char* pstring; const pstring c ...

  10. Docker从入门到部署项目

    Docker概念 Docker是一个开源的应用容器引擎,它是基于Go语言并遵从Apache2.0协议开源.Docker可以让开发者打包他们的应用以及依赖包到一个轻量级.可移植的容器中,然后发布到任何流 ...