常用算法代码模板及代码技巧
参考《AcWing算法基础课》、《AcWing算法提高课》等课程的思路,并结合日常刷题实践,我将常用的算法模板进行了系统整理。
现分享如下,既为方便自己查阅,也希望能为大家的学习提供一份参考。
基础算法
快速排序
模板
// do-while版本
void quick_sort(int q[], int l, int r)
{
if (l>=r) return;
int rnd_idx = rand() % (r-l+1)+l;
swap(q[l], q[rnd_idx]);
int x=q[l], i=l-1, j=r+1;
while (i<j)
{
do i++; while (q[i]<x);
do j--; while (q[j]>x);
if (i<j) swap(q[i], q[j]);
}
quick_sort(q, l, j);
quick_sort(q, j+1, r);
}
// while版本
void quick_sort(int q[], int l, int r)
{
if (l>=r) return;
int rnd_idx = rand() % (r-l+1)+l;
swap(q[l], q[rnd_idx]);
int x=q[l], i=l-1, j=r+1;
while (i<j)
{
while (q[++i]<x);
while (q[--j]>x);
if (i<j) swap(q[i], q[j]);
}
quick_sort(q, l, j);
quick_sort(q, j+1, r);
}
归并算法
模板
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
else tmp[k ++ ] = q[j ++ ];
while (i <= mid) tmp[k ++ ] = q[i ++ ];
while (j <= r) tmp[k ++ ] = q[j ++ ];
for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}
整数二分
模板
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
while (l < r)
{
int mid = l + r + 1 >> 1; //当l=mid时,一定要l+r+1
if (check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
浮点数二分
模板
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求,需要比预留的小数位+2
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
高精度加法
模板
//高精度数输入
void s2BIG(string s, int a[])
{
a[0] = s.size();
for (int i=1; i<=a[0]; i++)
a[i] = s[a[0]-i] - '0';
}
// 高精度数输出
void printBIG(int a[])
{
for (int i=a[0]; i>=1; i--)
cout << a[i];
cout << endl;
}
//低精度转高精度
void i2BIG(int n, int a[])
{
while (n>0)
{
a[++a[0]] = n % 10;
n /= 10;
}
if (a[0] == 0) a[0] = 1;
}
//高精加
void addBIG(int a[], int b[], int c[])
{
c[0] = max(a[0], b[0]);
int u = 0; // u存储低位向高位的进位
for (int i=1; i<=c[0]; i++)
{
int t = a[i] + b[i] + u;
c[i] = t % 10;
u = t / 10;
}
if (u>0) c[++c[0]] = u;
}
高精度减法
模板
//高精比较
bool cmpBIG(int a[], int b[])
{ // 判断高精度数字a是否小于高精度数字b
if (a[0] != b[0]) return a[0]<b[0];
for (int i=a[0]; i>=1; i--)
if (a[i] != b[i])
return a[i] < b[i];
return false;
}
//高精减
void subBIG(int a[], int b[], int c[])
{
c[0] = max(a[0], b[0]);
int u = 0;
for (int i=1; i<=c[0]; i++)
{
int t = a[i]-b[i]-u;
if (t<0)
{
u = 1;
c[i] = t + 10;
}
else
{
u = 0;
c[i] = t;
}
}
while (c[c[0]] == 0 && c[0]>1) c[0]--;
}
高精度乘法
模板
//高精乘(高精*低精)
void mulBIG(int a[], int b, int c[])
{
c[0] = a[0];
int u = 0;
for (int i=1; i<=c[0]; i++)
{
int t = a[i] * b + u;
c[i] = t % 10;
u = t / 10;
}
while (u>0)
{
c[++c[0]] = u % 10;
u /= 10;
}
}
//高精乘(高精*高精)
void mulBIG2(int a[], int b[], int c[])
{
c[0] = a[0] + b[0];
int u = 0;
for (int j=1; j<=b[0]; j++)
{
int u = 0;
for (int i=1; i<=a[0]; i++)
{
int t = a[i]*b[j] + c[i+j-1] + u;
c[i+j-1] = t % 10;
u = t / 10;
}
if (u>0)
c[a[0]+j] += u;
}
while (c[0]>1 && c[c[0]]==0)
{
c[0]--;
}
}
高精度除法
模板
//高精度除法
void divBIG1(int a[], int b, int c[])
{ // 将高精度数字a[]与低精度数字b相除,所得的商存入高精度数组c[]中
c[0] = a[0];
int r = 0;
for(int i = c[0]; i >= 1; i--)
{
int t = r * 10 + a[i];
c[i] = t / b;
r = t % b;
}
while(c[c[0]] == 0 && c[0] > 1) c[0]--;
}
void divBIG2(int a[], int b[], int c[])
{ // 将高精度数字a[]与高精度数字b[]相除,所得的商存入高精度数组c[]中
// int temp[1005] = {0}; // 临时数组
// int count[1005] = {0}; // 计数数组
c[0] = 1; // 初始商为0
c[1] = 0;
// 特殊情况处理
if(cmpBIG(a, b))
{ // 如果a < b,直接返回0
c[0] = 1;
c[1] = 0;
return;
}
// 计算商的位数
c[0] = a[0] - b[0] + 1;
for(int i = 1; i <= c[0]; i++) c[i] = 0;
// 模拟竖式除法
for(int i = c[0]; i >= 1; i--)
{
// 调整除数位数
int tempB[1005] = {0};
tempB[0] = b[0] + i - 1;
for(int j = 1; j <= b[0]; j++)
tempB[j+i-1] = b[j];
// 试商
while(!cmpBIG(a, tempB))
{
// a = a - tempB
subBIG(a, tempB, a);
c[i]++; // 商对应位加1
// 处理进位
int j = i;
while(c[j] > 9)
{
c[j+1] += c[j] / 10;
c[j] %= 10;
j++;
if(j > c[0]) c[0] = j;
}
}
}
// 去除前导零
while(c[c[0]] == 0 && c[0] > 1) c[0]--;
}
数据结构
单调队列
模板
struct Node
{
int v, idx;
};
deque<Node> dq;
for (int i = 1; i <= n; i++) // 遍历数组
{
while (!dq.empty() && dq.front().idx <= i - k)
dq.pop_front();
// 维护minq队列:保持队列单调递增
// 如果修改为dq.back().v <= a[i],则维护maxq队列:保持队列单调递减
while (!dq.empty() && dq.back().v >= a[i])
dq.pop_back();
dq.push_back({a[i], i}); // dq.front().v为最小值
// 按照题目要求输出最小值、最大值等
}
动态规划
线性DP
模板1
// 状态表示
dp[i]表示以第i项结尾的所有子段和的最大值
// 初始状态
dp[0] = 0
// 状态转移
dp[i] = a[i] // 以第i项作为新子段
dp[i] = dp[i-1] + a[i] // 将第i项接在前面的子段尾部
// 结果
max{dp[i]}
模板2
// 状态表示
dp[i]表示前i个数字共有多少种翻译方法
// 初始状态
dp[0] = 1, dp[1] = 1
// 状态转移
dp[i] = dp[i-1] + dp[i-2] // if (s[i-1]=='1' || s[i-1]=='2' && s[i]<='5',s[i]和s[i-1]合并翻译,s[i]单独翻译
dp[i] = dp[i-1] // else s[i]只能单独翻译
// 结果
dp[n]
模板3
// 状态表示
dp[i]表示前i个数字组成的数列共有多少种划分方法
// 初始状态
dp[0] = 1
// 状态转移
dp[i] = dp[i-1] // a[i]单独一组; a[i]<=s; sum[i]-sum[i-1]<=s;
dp[i] = dp[i-2] // a[i-1]和a[i]组成一组; a[i-1]+a[i]<=s; sum[i]-sum[i-2]<=s;
dp[i] = dp[i-3] // a[i-2]、a[i-1]和a[i]组成一组; a[i-2]+a[i-1]+a[i]<=s; sum[i]-sum[i-3]<=s;
...
dp[i] = dp[k] // a[k+1]、...、a[i-1]和a[i]组成一组; a[k+1]+...+a[i-1]+a[i]<=s; sum[i]-sum[k]<=s;
...
dp[i] = dp[0] // a[1]、a[2]、...、a[i-1]和a[i]组成一组; a[1]+a[2]+...+a[i-1]+a[i]<=s; sum[i]-sum[0]<=s;
// 结果
dp[n]
模板4
// 状态表示
dp[i]表示前i个字母组成的字符串可以被划分成的最少的单词数
// 初始状态
memset(dp, 0x3f, sizeof(dp)); dp[0] = 0;
// 状态转移
dp[i] = dp[0] + 1; // s[1]~s[i]是字典中的某个单词
dp[i] = dp[1] + 1; // s[2]~s[i]是字典中的某个单词
...
dp[i] = dp[k] + 1; // s[k+1]~s[i]是字典中的某个单词
...
dp[i] = dp[i-1] + 1; // s[i]是字典中的某个单词
// 结果
dp[m+1]
最长上升子序列
模板1
// 状态表示
dp[i]表示以a[i]为结尾的最长上升子序列的长度
// 初始状态
dp[i] = 1 (1<=i<=n)
// 状态转移
dp[i] = dp[1] + 1; a[1]<a[i]
dp[i] = dp[2] + 1; a[2]<a[i]
...
dp[i] = dp[j] + 1; a[j]<a[i]
...
dp[i] = dp[i-1] + 1; a[i-1]<a[i]
dp[i] = max(dp[i], dp[j] + 1); (1<=j<=i-1, a[j]<a[i])
// 结果
max{dp[i]}
// 其他变形
最长下降子序列:a[j]>a[i]
最长不下降子序列:a[j]<=a[i]
最长不上升子序列:a[j]>=a[i]
模板2
// 状态表示
dp[i]表示以a[i]为结尾的上升子序列的和的最大值
// 初始状态
dp[i] = a[i] (1<=i<=n)
// 状态转移
dp[i] = max(dp[i], dp[j] + a[i]); (1<=j<=i-1, a[j]<a[i])
// 结果
max{dp[i]}
最长上升子序列二分优化
模板
// 最长上升子序列
len = 0;
for (int i=1; i<=n; i++)
{
if (a[i]>b[len])
{
b[++len] = a[i];
dp[i] = len;
}
else
{
int m = lower_bound(b+1, b+len+1, a[i]) - b;
b[m] = a[i];
dp[i] = m;
}
}
// 最长不下降子序列
len = 0;
for (int i=1; i<=n; i++)
{
if (a[i]>b[len])
{
b[++len] = a[i];
dp[i] = len;
}
else
{
int m = upper_bound(b+1, b+len+1, a[i]) - b;
b[m] = a[i];
dp[i] = m;
}
}
01背包
模板1
// 最值型01背包
// 朴素解法
for (int i=1; i<=n; i++)
{
for (int j=0; j<=m; j++)
{
f[i][j] = f[i-1][j];
if (j>=v[i])
f[i][j] = max(f[i][j], f[i-1][j-v[i]]+w[i]);
}
}
// 一维数组优化
for (int i=1; i<=n; i++)
for (int j=m; j>=v[i]; j--)
f[j] = max(f[j], f[j-v[i]]+w[i]);
模板2
// 布尔型01背包
dp[j] |= dp[j-w[i]]
模板3
// 计数型01背包
dp[j] += dp[j-w[i]]
完全背包
模板1
// 最值型完全背包
// 朴素解法
for (int i=1; i<=n; i++)
for (int j=0; j<=m; j++)
{
dp[i][j] = dp[i-1][j];
if (j>=v[i])
dp[i][j] = max(dp[i][j], dp[i][j-v[i]]+w[i]);
}
// 一维数组优化
for (int i=1; i<=n; i++)
for (int j=v[i]; j<=m; j++)
dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
模板2
// 布尔型完全背包
dp[j] |= dp[j-w[i]]
模板3
// 计数型完全背包
dp[j] += dp[j-w[i]]
dp[j] = min(dp[j], dp[j-w[i]]+1)
浙公网安备 33010602011771号