将问题分解为若干个同种子问题
自己调用自己
所有递归都可以转化为递归搜索树来理解
斐波那契数列
AcWing 717. 简单斐波那契 - AcWing
int f(int n)
{
if(n == 1) return 0;
if(n == 2) return 1;
return f(n - 1) + f(n - 2);
}
指数型枚举
AcWing 92. 递归实现指数型枚举 - AcWing
const int N = 16;
int n;
bool sta[N];
void dfs(int u)
{
if (u > n)
{
for (int i = 1; i <= n; i++)
{
if (sta[i])
cout << i << ' ';
}
cout << endl;
return;
}
//选
sta[u] = true;
dfs(u + 1);
sta[u] = false;
//不选
dfs(u + 1);
}
组合型枚举
AcWing 93. 递归实现组合型枚举 - AcWing
const int N = 30;
int sta[N], m, n;
bool used[N];
void dfs(int u, int start)
{
if (u > n)
{
for (int i = 1; i <= n; i++)
printf("%d ", sta[i]);
puts("");
return ;
}
for (int i = start; i <= m; i++)
{
sta[u] = i;
dfs(u + 1, i + 1);
sta[u] = 0;
}
}
优化:剪枝
if(u + m - start < n)
return ;
排列型枚举
AcWing 94. 递归实现排列型枚举 - AcWing
const int N = 10;
int n;
int sta[N];
bool use[N];
void dfs(int u)
{
if (u > n)
{
for (int i = 1; i <= n; i++)
cout << sta[i] << ' ';
cout << endl;
return;
}
for (int i = 1; i <= n; i++)
if (!use[i])
{
sta[u] = i;
use[i] = true;
dfs(u + 1);
sta[u] = 0;
use[i] = false;
}
}
先求子问题,用子问题推原问题
斐波那契数列
int main()
{
int n;
cin >> n;
int f[46];
f[1] = 0, f[2] = 1;
for(int i = 3; i <= n; i++)
f[i] = f[i - 1] + f[i - 2];
for(int i = 1; i <= n; i++)
cout << f[i] << ' ';
return 0;
}
优化:不开数组,使用两个变量(滚动数组雏形)
int main()
{
int n;
cin >> n;
int a = 0, b = 1;
for(int i = 1; i <= n; i++)
{
cout << a << '';
int fn = a + b;
a = b, b
}
return 0;
}
AcWing 95. 费解的开关 - AcWing
AcWing 1209. 带分数 - AcWing
AcWing 116. 飞行员兄弟 - AcWing
AcWing 1208. 翻硬币 - AcWing
整数二分
步骤:
找一个区间[L, R],使得答案一定在区间中
找一个判断条件, 使得该判断体哦阿健具有二段性,并且答案一定是改二段性的分界点。
分析重点M在改判断条件下是否成立,考虑在哪个区间。
如果更新方式是 R = M,则不用做任何处理;如果是 L= M, 则需要在计算M时加1。
模板:
将[l, r]划分为[l, M]和[M + 1, r]
bool check(int x)
{
/*
是否满足某种性质
*/
}
int bsearch_1(int l, int r)
{
while(l < r)
{
int M = l + r >> 1; //右移
if(check(M)) r = M;
else l = M + 1;
}
return l;
}
将[l, r]划分为[l, M - 1]和[M, r]
bool check(int x)
{
/*
是否满足某种性质
*/
}
int bsearch_2(int l, int r)
{
while(l < r)
{
int M = l + r + 1 >> 1; //右移
if(check(M)) l = M;
else r = M - 1;
}
return l;
}
实数二分
模板:
bool check(int x)
{
/*
是否满足某种性质
*/
}
int bsearch_3(double l, double r)
{
const double eps = 1e-6;
while(r - l > eps)
{
double mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid;
}
return l;
}
对数据预处理
一维前缀和
S[i] = a[1] + a[2] + a[3] + ... + a[i] a[l] + ... + a[r] = S[r] - S[l - 1]
int n, m;
int a[N]; // 表示原数组
int s[N]; // 表示前缀和数组
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
s[i] = s[i - 1] + a[i];
}
//合并写
for(int i = 1; i <= n; i++)
{
scanf("%d", &s[i]);
s[i] += s[i - 1];
}
while (m -- )
{
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
二维前缀和
计算前缀和
S[x][y] = S[x - 1][y] + S[x][y - 1] - S[x - 1][y - 1] + a[x][y]
计算子矩阵和
S[x, y]为第x行y列左上部分所有元素的和 以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
AcWing 789. 数的范围 - AcWing
AcWing 790. 数的三次方根 - AcWing
AcWing 795. 前缀和 - AcWing
AcWing 796. 子矩阵的和 - AcWing
AcWing 730. 机器人跳跃问题 - AcWing
AcWing 1221. 四平方和 - AcWing
AcWing 1227. 分巧克力 - AcWing
AcWing 99. 激光炸弹 - AcWing
AcWing 1230. K倍区间 - AcWing
如果 a,b 均是正整数且互质,那么由ax + by, x ≥ 0, y ≥ 0不能凑出的最大数是 ab − a − b。
DP问题 一般为最值和个数。
从集合角度分析DP问题
化零为整:状态表示
集合
属性:Max/Min/Count
化整为零:状态计算
集合的划分(划分依据:找最后一个不同点)
0 - 1背包:每件物品最多使用一次
有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。 输出最大价值。
完全背包:每件物品使用不限次数
多重背包:每件物品的使用次数有限制
优化
分组背包:有N组, 每组有若种,每种有若干个
AcWing 1205. 买不到的数目 - AcWing
AcWing 1211. 蚂蚁感冒 - AcWing
AcWing 1216. 饮料换购 - AcWing
AcWing 2. 01背包问题 - AcWing
AcWing 1015. 摘花生 - AcWing
AcWing 895. 最长上升子序列 - AcWing
AcWing 1212. 地宫取宝 - AcWing
AcWing 1214. 波动数列 - AcWing
例题
AcWing 1210. 连号区间数 - AcWing
AcWing 1236. 递增三元组 - AcWing
AcWing 1245. 特别数的和 - AcWing
AcWing 1204. 错误票据 - AcWing
AcWing 466. 回文日期 - AcWing
枚举日期做法
AcWing 466. 回文日期 - AcWing
AcWing 787. 归并排序 - AcWing
分治思想
找分界点mid = l + r >> 1
递归排序left, right
归并——合二为一:利用双指针
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 i = l, j = mid + 1, k = 0;
while (i <= mid && j <= r)
if (q[i] <= q[j]) tem[k++] = q[i++];
else tem[k++] = q[j++];
while (i <= mid) tem[k++] = q[i++];
while (j <= r) tem[k++] = q[j++];
for (int i = l, j = 0; i <= r; j++) q[i] = tem[j];
}
AcWing 1219. 移动距离 - AcWing
AcWing 1229. 日期问题 - AcWing
AcWing 1231. 航班时间 - AcWing
AcWing 1241. 外卖店优先级 - AcWing
AcWing 788. 逆序对的数量 - AcWing
应用范围 :线段树包含树状数组
代码短, 常数小
O(longn):给某个位置上的数加上一个数(单点修改), 求某一个前缀和(区间查询)
/*
数组下标为奇数,树状数组与原数组相同.
判断c[x]为第几层 :x二进制末尾有多少个0,假设x二进制末尾有k个0,则c[x]表示(x - 2 ^ k, x]的和(2 ^ k = lowbit(x))。
*/
int arr[N], tr[N];
int lowbit(int x)
{
return x & -x;
}
void add(int x, int v)
{
for (int i = x; i <= n; i += lowbit(i)) tr[i] += v;
}
//求和
int query(int x)
{
int res = 0;
for (int i = x; i ; i -= lowbit(i)) res += tr[i];
return res;
}
int main()
{
//初始化树状数组
for (int i = 1; i <= n; i++) add(i, arr[i]);
}
AcWing 1264. 动态求连续区间和 - AcWing
AcWing 1265. 数星星 - AcWing
是二叉树
操作:
单点修改(O(logn)):递归+回溯
区间查询(O(logn)):递归
区间修改:需用懒标记,复杂
函数
pushup:用子节点信息更新当前节点信息(有时省略)
build:在一段区间上初始化线段树
modify:修改
query:查询
存储
与堆的存储方式类似, 存在一维数组内
下标为x:父节点为x / 2下取整(x >> 1), 左儿子为2 * x(x << 1), 右儿子为2 * x + 1(x << 1 | 1)
const int N = 100010;
int n, m;
int a[N];
struct Node
{
int l, r;
int sum;
}tr[4 * N];
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r)
{
if (l == r) tr[u] = { l, r, a[r] };
else
{
tr[u] = { l, r };
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
int query(int u, int l, int r) //根节点为u 查询[l, r]的和
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
int mid = tr[u].l + tr[u].r >> 1;
int sum = 0;
if (l <= mid) sum += query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
void modify(int u, int x, int v) //根节点为u 在x位置上加v
{
if (tr[u].l == tr[u].r) tr[u].sum += v;
else
{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
AcWing 1264. 动态求连续区间和 - AcWing
AcWing 1270. 数列区间最大值 - AcWing
AcWing 1215. 小朋友排队 - AcWing 归并排序求逆序对
AcWing 1228. 油漆面积 - AcWing 扫描线法 + “非常特殊的线段树”(懒标记不传递)
AcWing 1238. 日志统计 - AcWing
每次取出队头元素,将扩展出的所有元素放在队尾。
组成:判重数组(一般入队时判重),队列
框架
queue <- 初始状态;
while(queue非空)
{
t <- 队头;
队头出队;
for(扩展t)
{
ver <- 新节点;
if(!st[ver]) ver -> 队尾;
}
}
AcWing 1101. 献给阿尔吉侬的花束 - AcWing
Flood Fill的实现
BFS DFS
AcWing 1113. 红与黑 - AcWing
AcWing 1224. 交换瓶子 - AcWing
交换同一环内的点:裂成两个环
交换不同环中的点:并成一个环
AcWing 1240. 完全二叉树的权值 - AcWing
AcWing 1096. 地牢大师 - AcWing
AcWing 1233. 全球变暖 - AcWing
AcWing 1207. 大臣的旅费 - AcWing
AcWing 826. 单链表 - AcWing
AcWing 1055. 股票买卖 II - AcWing
AcWing 104. 货仓选址 - AcWing
AcWing 122. 糖果传递 - AcWing
AcWing 112. 雷达设备 - AcWing
该题策略:
将所有区间右端点排序
扫描每个线段
上一个点不在区间中,则选右端点
上一个点在区间中, 则跳过该区间
AcWing 1235. 付账问题 - AcWing
AcWing 1239. 乘积最大 - AcWing
AcWing 1247. 后缀表达式 - AcWing
AcWing 1248. 灵能传输 - AcWing
欧几里德算法——辗转相除法
(a, b) = (a, a mod b)
//直接用
__gcd();
int gcd(int a, int b)
{
return b ? gcd(b, a % b) : a;
}
AcWing 1246. 等差数列 - AcWing
扩展——裴蜀定理
/*
指对于任意正整数对(a,b),一定存在非零整数x和y,使得 ax+by=gcd(a,b);
分情况讨论一下扩展欧几里得定理:
(1)当b = 0时,a和b的最大公约数为a.则x = 1;
(2)当b ≠ 0时,by+(a mod b)x = gcd(a,b)
->by+(a - a/b * b)x = gcd(a,b)
->ax+b(y - a/b * x) = gcd(a,b)
即当我们用扩展欧几里得定理求x和y时,欧几里得定理每递归一次x不用变,y->y-a/b * x即可
如果我们求出x和y的一对,我们记为x0和y0
那么其他的x和y可以通过x0,y0表示:
令a’=a/gcd(a,b) , b’=b/gcd(a,b);
那么其他的x和y可以表示为:x=x0+kb’,y=y0-ka’;
证明:
ax0+by0 = gcd(a,b)
ax+by = gcd(a,b)
如果x0比x小,那么y0就应该比y大,这样加起来的结果才可能相同
所以,a(x-x0) = b(y0-y);
两边同时除以一个gcd(a,b)
得到:a’(x-x0) = b’(y0-y)
那么b’就应该能整除a’(x-x0),又因为a’和b’互质,所以b’应该能整除(x-x0)
所以(x-x0) = kb’
则 x = x0+kb’
y = y0-ka’
这里的k可正可负
*/
int exgcd(int a, int b, int &x, int &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
AcWing 1299. 五指山 - AcWing
N = P1 ^ α1 + P2 ^ α2 + P3 ^ α3 + ... + Pk ^ αk
AcWing 1295. X的因子链 - AcWing
筛法求素数——线性筛法(欧拉筛法)
可在O(n)下求出1~n中所有质数, 以及每个数的最小质因子
int primes[N], cnt; //存所有质数
bool st[N]; //当前质数是否被筛
int minp[N];
int get_primes(int n)
{
for(int i = 2; i <= n; i++)
{
if(!st[i]) minp[i] = i, primes[cnt++] = i;
for(int j = 0; primes[j] * i <= n; j++)
{
st[primes[j] * i] = true;
minp[primes[j] * i] = primes[j];
if(i % primes[j] == 0) break;
}
}
}
约数个数:(α1 + 1)(α2 + 1)(α3 + 1) ... (αk + 1)
约数之和:(1 + P1 + P1 ^ 2 + P1 ^ 3 + ... + P1 ^ α1)(1 + P2 + P2 ^ 2 + P2 ^ 3 + ... + P2 ^ α2) ... (1 + Pk + Pk ^ 2 + Pk ^ 3 + ... + Pk ^ αk)
AcWing 1223. 最大比例 - AcWing
AcWing 1301. C 循环 - AcWing
AcWing 1225. 正则问题 - AcWing
AcWing 1243. 糖果 - AcWing
AcWing 1050. 鸣人的影分身 - AcWing
AcWing 1047. 糖果 - AcWing
AcWing 1222. 密码脱落 - AcWing
较优循环方式
循环区间长度, 枚举左端点, 求出右端点
AcWing 1220. 生命之树 - AcWing
递归
AcWing 1242. 修改数组 - AcWing 并查集
并查集
普通并查集:p[i]是i的父节点,i所在树的根节点是代表元素
单链表式并查集:p[i]是单链表中的下一个节点,i所在树的根节点是从i开始向右找,第一个没有被用过的位置
一般ACM或者笔试题的时间限制是1秒或2秒。 在这种情况下,C++代码中的操作次数控制在 10^7 ∼ 10^8为最佳。下面给出在不同数据范围下,代码的时间复杂度和算法该如何选择:
n≤30, 指数级别, dfs+剪枝,状态压缩dp n≤10^2 => O(n^3),floyd,dp,高斯消元 n≤10^3=> O(n^2),O(n^2logn),dp,二分,朴素版Dijkstra、朴素版Prim、Bellman-Ford n≤10^4 => O(n∗√n),块状链表、分块、莫队 n≤10^5 => O(nlogn) => 各种sort,线段树、树状数组、set/map、heap、拓扑排序、dijkstra+heap、prim+heap、Kruskal、spfa、求凸包、求半平面交、二分、CDQ分治、整体二分、后缀数组、树链剖分、动态树 n≤10^6 => O(n), 以及常数较小的 O(nlogn)算法 => 单调队列、 hash、双指针扫描、并查集,kmp、AC自动机,常数比较小的 O(nlogn) 的做法:sort、树状数组、heap、dijkstra、spfa n≤10^7 => O(n),双指针扫描、kmp、AC自动机、线性筛素数 n≤10^9 => O(√n),判断质数 n≤10^18 => O(logn),最大公约数,快速幂,数位DP n≤10^1000 => O((logn)^2),高精度加减乘除 n≤10^100000 => O(logk×loglogk),k表示位数,高精度加减、FFT/NTT
输入规模小于10^5 时,cin和cout与scanf和printf效率相似;而大于10^5 时,prinf和scanf效率高出一倍。
全局变量初值为0,局部变量初值为随机数。
上取整
(int)ceil(a / b); (a + b - 1) / b;
使模运算结果始终为正数
( a % b + b) % b;
排序
sort();`
字符型变整型
int x = 0; for(int i = 0; i < str.size(); i++) x = x * 10 + str[i] - '0';
或使用 stoi函数
读取整行数据
#include
#include
#include
#include
using namespace std;
int a[N],
int main()
{
int cnt; //行数
cin >> cnt;
string line;
getline(cin, line); //忽略掉第一行回车
while(cnt --)
{
getline(cin, line);
stringstream ssin(line);
while(ssin >> a[n]) n++;
}
return 0;
}
二元组排序
结构体
pair(默认以first为第一关键字排序)
队列:先进先出 bfs
栈:先进后出 递归 dfs
结构体内嵌套比较函数
struct Segment
{
double l, r;
bool operator< (const Segment& t)const
{
return r < t.r;
}
}s;
~i 等价于 i != -1;
快速幂
int quick_power(int a, int k, int p) // 求a^k mod p
{
L res = 1 % p;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
避免处理浮点数:把除化为乘 AcWing 3490. 小平方 - AcWing