voidquick_sort(int q[], int l, int r){ // 没有数或者只有一个数 if (l >= r) return; int i = l - 1, j = r + 1, x = q[l + 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); }
voidquick_sort(int q[], int l, int r){ // 没有数或者只有一个数 if (l >= r) return; // 定义两个指针,定义区间分割点 x int i = l - 1, j = r + 1, x = q[l + r >> 1]; // 两个指针向中间靠 while (i < j){ // 找到第一个 q[i] >= x do i ++ ; while (q[i] < x); // 找到第一个 q[j] <= x do j -- ; while (q[j] > x); // 两个指针 i j 都找到了当前不符合的数的位置,则交换它们 if (i < j) swap(q[i], q[j]); } // 递归处理左右两段 quick_sort(q, l, j), quick_sort(q, j + 1, r); }
intmain(){ cin >> n; for (int i = 0; i < n; i ++ ){ cin >> q[i]; } quick_sort(q, 0, n - 1); for (int i = 0; i < n; i ++ ) cout << q[i] << ' '; return0; }
1.2归并排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
voidmerge_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]; }
voidmerge_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; // k 代表已经合并了多少个数,i 指向左半边的起点,j 指向右半边的起点 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]; }
intmain(){ cin >> n; for (int i = 0; i < n; i ++ ){ cin >> q[i]; } merge_sort(q, 0, n - 1); for (int i = 0; i < n; i ++ ) cout << q[i] << ' '; return0; }
// 区间[l, r]被划分为[l, mid]和[mid + 1, r]时使用 intbsearch_1(int l, int r){ while (l < r){ int mid = l + r >> 1; if (check(mid)) r = mid; else l = mid + 1; } return l; } // 区间[l, r]被划分为[l, mid - 1]和[mid, r]时使用 intbsearch_2(int l, int r){ while (l < r){ int mid = l + r + 1 >> 1; if (check(mid)) l = mid; else r = mid - 1; } return l; }
intmain(){ scanf("%d%d", &n, &m); for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); while (m -- ){ int x; scanf("%d", &x); int l = 0, r = n - 1; // 我们二分查找的是下标,而下标的范围是0~n-1 while (l < r){ int mid = l + r >> 1; if (q[mid] >= x) r = mid; else l = mid + 1; } if (q[l] != x) cout << "-1 -1" << endl; // 二分确定的不是答案,返回找不到 else{ cout << l << ' '; // 找到了左端点,先输出即可 int l = 0, r = n - 1; // 再次初始化查找右端点 while (l < r){ int mid = r + l + 1 >> 1; if (q[mid] <= x) l = mid; else r = mid - 1; } cout << r << endl; } } return0; }
1.4浮点数二分算法
1 2 3 4 5 6 7 8 9 10 11
boolcheck(double x){/*...*/} //检查x是否满足某种性质
doublebsearch_3(double l, double r){ constdouble eps = 1e-6; // eps表示精度,取决于题目对精度的要求 while (r - l > eps){ double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l; }
1.5高精度加法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// C = A + B, A >= 0, B >= 0 vector<int> add(vector<int> &A, vector<int> &B){ if (A.size() < B.size()) returnadd(B, A); vector<int> C; int t = 0; for (int i = 0; i < A.size(); i ++ ){ t += A[i]; if (i < B.size()) t += B[i]; C.push_back(t % 10); t /= 10; } if (t) C.push_back(t); return C; }
1.6高精度减法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// C = A - B,满足A >= B, A >= 0, B >= 0 vector<int> sub(vector<int> &A, vector<int> &B){ vector<int> C; for (int i = 0, t = 0; i < A.size(); i ++ ){ t = A[i] - t; if (i < B.size()) t -= B[i]; C.push_back((t + 10) % 10); if (t < 0) t = 1; else t = 0; } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
1.7高精度乘以低精度
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// C = A * b, A >= 0, b >= 0 vector<int> mul(vector<int> &A, int b){ vector<int> C; int t = 0; for (int i = 0; i < A.size() || t; i ++ ){ if (i < A.size()) t += A[i] * b; C.push_back(t % 10); t /= 10; } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
1.8高精度除以低精度
1 2 3 4 5 6 7 8 9 10 11 12 13
// A / b = C ... r, A >= 0, b > 0 vector<int> div(vector<int> &A, int b, int &r){ vector<int> C; r = 0; for (int i = A.size() - 1; i >= 0; i -- ){ r = r * 10 + A[i]; C.push_back(r / b); r %= b; } reverse(C.begin(), C.end()); while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
// 区间[l, r] 内全部加上c voidinsert(int l, int r, int c){ b[l] += c; b[r + 1] -= c; }
intmain(){ sacnf("%d%d", &n, &m); for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]); for (int i = 1; i <= n; i ++ ) insert(i, i, a[i]); // m个操作 while (m -- ){ int l, r, c; scanf("%d%d%d", &l, &r, &c); insert(l, r, c); } // 再求原来的数组 for (int i = 1; i <= n; i ++ ) b[i] += b[i - 1]; for (int i = 1; i <= n; i ++ ) printf("%d ", b[i]); return0; }
intmain(){ int m; cin >> m; while (m -- ){ int x; cin >> x; int res = 0; while (x) x -= lowbit(x), res ++ ; // 每次减去x的最后一位1 cout << res << ' '; } return0; }
1.14双指针算法
1.14.1模板
1 2 3 4 5 6 7
for (int i = 0, j = 0; i < n; i ++ ){ while (j < i && check(i, j)) j ++; // 具体问题的逻辑 } 常见问题分类: (1)对于一个序列,用两个指针维护一段区间 (2)对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
intmain(){ scanf("%d", &n); for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]); int res = 0; for (int i = 0, j = 0; i < n; i ++ ){ s[q[i]] ++ ; while (j < i && s[q[i]] > 1) s[q[j ++ ]] -- ; res = max(res, i - j + 1); } cout << res << endl; return0; }
intmain(){ scanf("%d%d%d", &n, &m, &x); for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]); for (int i = 0; i < m; i ++ ) scanf("%d", &b[i]); for (int i = 0, j = m - 1; i < n; i ++ ){ // 因为保证一定有解 while (a[i] + b[j] > x) j -- ; if (a[i] + b[j] == x){ printf("%d %d\n", i, j); break; } } return0; }
// 二分求出x对应的离散化的值 intfind(int x){ // 找到第一个大于等于x的值 int l = 0, r = alls.size() - 1; while (l < r){ int mid = l + r >> 1; if (alls[mid] >= x) r = mid; else l = mid + 1; } return r + 1; // 映射到1,2,...n }
// 求x离散化后的值 intfind(int x){ int l = 0, r = alls.size() - 1; while (l < r){ int mid = l + r >> 1; if (alls[mid] >= x) r = mid; // 找到的是大于x的最小的数 else l = mid + 1; } return r + 1; }
intmain(){ cin >> n >> m; for (int i = 0; i < n; i ++ ){ int x, c; cin >> x >> c; adds.push_back({x, c}); alls.push_back(x); // 加入到待离散化的数组中 } for (int i = 0; i < m; i ++ ){ int l, r; cin >> l >> r; query.push_back({l, r}); alls.push_back(l); // 查询的端点区间也需要离散化 alls.push_back(r); } // 去重 sort(alls.begin(), alls.end()); alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 遍历所有操作 for (auto item : add){ int x = find(item.first); a[x] += item.second; } // 预处理前缀和 for (int i = 1; i <= alls.size(); i ++ ) s[i] = s[i - 1] + a[i]; // 处理询问 for (auto item : query){ int l = find(item.first), r = find(item.second); cout << s[r] - s[l - 1] << endl; } return0; }
1.16区间合并
1.16.1模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// 将所有存在交集的区间合并 voidmerge(vector<PII> &segs){ vector<PII> res; sort(segs.begin(), segs.end()); int st = -2e9, ed = -2e9; for (auto seg : segs){ if (ed < seg.first){ if (st != -2e9) res.push_back({st, ed}); st = seg.first, ed = seg.second; }else ed = max(ed, seg.second); } if (st != -2e9) res.push_back({st, ed}); segs = res; }
int son[N][26], cnt[N], idx; // 0号点既是根结点,又是空节点 // son[][]存储树中每个节点的子节点 // cnr[]存储每个节点结尾的单词数量
// 插入一个字符串 voidinsert(char *str){ int p = 0; for (int i = 0; str[i]; i ++ ){ int u = str[i] - 'a'; if (!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; } cnt[p] ++ ; }
// 查询字符串出现的次数 intquery(char *str){ int p = 0; for (int i = 0; str[i]; i ++ ){ int u = str[i] - 'a'; if (!son[p][u]) return0; p = son[p][u]; } return cnt[p]; }
// 交换两个点,及其映射关系 voidheap_swap(int a, int b){ swap(ph[hp[a]], ph[hp[b]]); swap(hp[a], hp[b]); swap(h[a], h[b]); }
voiddown(int u){ int t = u; if (u * 2 <= size && h[u * 2] < h[t]) t = u * 2; if (u * 2 + 1 <= size && h[u * 2 + 1] < h[t]) t = u * 2 + 1; if (u != t){ heap_swap(u, t); down(t); } }
voidup(int u){ while (u / 2 && h[u] < h[u / 2]){ heap_swap(u, u / 2); u >>= 1; } }
voidadd(int a, int b){e[idx] = b, ne[idx] = h[a], h[a] = idx ++; }
// 以u为根的子树的点的数量 intdfs(int u){ st[u] = true; int sum = 1, res = 0; // 当前点的数量,每一个连通块点数量的最大值 for (int i = h[u]; i != -1; i = ne[i]){ int j = e[i]; if (!st[j]){ int s = dfs(j); // 当前子树的大小 res = max(res, s); // 当前子树也相当于一个连通块,需要比较 sum += s; // 当前子树是以u为根的子树的点的数量的一部分,需要加和 } } // 外层的那个剩余的部分 res = max(res, n - sum); ans = min(ans, res); return sum; }
intmain(){ cin >> n >> m; memset(h, -1, sizeof h); for (int i = 0; i < n - 1; i ++ ){ int a, b; cin >> a >> b; add(a, b), add(b, a); } dfs(1); printf("%d\n", ans); return0; }
voiddfs(int u){ if (u == n){ for (int i = 0; i < n; i ++ ) printf("%d", path[i]); puts(""); return; } for (int i = 1; i <= n; i ++ ){ if (!st[i]){ path[u] = i; st[i] = true; dfs(u + 1); path[u] = 0; st[i] = false; } } }
while (q.size()){ int t = q.front(); q.pop(); for (int i = h[t]; i != -1; i = ne[i]){ int j = e[i]; if (!st[j]){ st[j] = true; // 表示点j已经被遍历过 q.push(j); } } }
voidadd(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ; }
intbfs(){ queue<int> q; q.push(1); memset(d, -1, sizeof d); d[1] = 0; while (q.size()){ auto t = q.front(); q.pop(); for (int i = h[t]; i != -1; i = ne[i]){ int j = e[i]; if (d[j] == -1){ d[j] = d[t] + 1; q.push(j); } } } return d[n]; }
intmain(){ memset(h, -1, sizeof h); cin >> n >> m; for (int i = 1; i <= m; i ++ ){ int a, b; cin >> a >> b; add(a, b); } cout << bfs() << endl; return0; }
intbellman_ford(){ memset(dist, 0x3f, sizeof dist); dist[1] = 0; for (int i = 0; i < k; i ++ ){ memcpy(backup, dist, sizeof dist); for (tin j = 0; j < m; j ++ ){ int a = edges[j].a, b = edges[j].b, w = edges[j].w; dist[b] = min(dist[b], backup[a] + w); // 只用上一次的结果,来更新当前的距离 } } if (dist[n] > 0x3f3f3f3f / 2) return-1; return dist[n]; }
intmain(){ scanf("%d%d%d", &n, &m, &k); for (int i = 0; i < m; i ++ ){ int a, b, w; scanf("%d%d%d", &a, &b, &m); edges[i] = {a, b, w}; } int t = bellman_ford(); if (t == -1) puts("impossible"); elseprintf("%d\n", t); return0; }
// 时间复杂度O(nm),n表示点数,m表示边数 int n; // 总点数 int h[N], w[N], e[N], ne[N], idx; // 邻接表存储所有边 int dist[N], cnt[N]; // dist[x]存储1号点到x的最短距离,cnt[x]存储1到x的最短路中经过的点数 bool st[N]; // 存储每个点是否在队列中
// 如果存在负环,则返回true, 否则返回false boolspfa(){ // 不需要初始化dist数组 // 原理:如果某条最短路径上有n个点(除了自己),那么加上自己之后一共有n + 1个点,由抽屉原理一定有两个点相同,所以存在环 queue<int> q; for (int i = 1; i <= n; i ++ ){ q.push(i); st[i] = true; } while (q.size()){ auto t = q.front(); q.pop(); st[t] = false; for (int i = h[t]; i != -1; i = ne[i]){ int j = e[i]; if (dist[j] > dist[t] + w[i]){ cnt[j] = cnt[t] + 1; if (cnt[j] >= n) returntrue; if (!st[j]){ q.push(j); st[j] = true; } } } } returnfalse; }
3.8floyd算法
1 2 3 4 5 6 7 8 9 10 11 12 13
// 时间复杂度是O(n^3),n表示点数 // 初始化 for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++ ) if (i == j) d[i][j] = 0; else d[i][j] = INF; // 算法结束后,d[a][b]表示a到b的最短距离 voidfloyd(){ for (int k = 1; k <= n; k ++ ) for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= n; j ++ ) d[i][j] = min(d[i][j], d[i][k] + d[k][j]); }
// 时间复杂度O(mlogm),n表示点数,m表示边数 int n, m; // n是点数,m是边数 int p[N]; // 并查集的父节点数组 structEdge{ int a, b, w; booloperator< (const Edge &W)const{ return W < W.w; } }edges[M];
intkruskal(){ sort(edges, edges + m); for (int i = 1; i <= n; i ++ ) p[i] = i; // 初始化并查集 int res = 0, cnt = 0; for (int i = 0; i < m; i ++ ){ int a = edges[i].a, b = edges[i].b, w = edges[i].w; a = find(a), b = find(b); if (a != b){ // 如果两个连通块不连通,则将这两个连通块合并 p[a] = b; res += w; cnt ++ ; } } if (cnt < n - 1) return INF; return res; }
第四章 数学知识
4.1试除法判定质数
1 2 3 4 5 6 7 8
boolis_prime(int x){ if (x < 2) returnfalse; // 第一个质数从2开始 for (int i = 2; i <= x / i; i ++ ){ // 枚举到sqrt(x)即可 if (x % i == 0) // 如果出现了1和它本身以外的约数,那么判断不是质数 returnfalse; } returntrue; }
boolis_prime(int x){ if (x < 2) returnfalse; for (int i = 2; i <= x / i; i ++ ){ if (x % i == 0) returnfalse; } returntrue; }
intmain(){ cin >> n; for (int i = 1; i <= n; i ++ ){ if (is_prime(i)) cout << i << endl; } return0; }
4.2试除法分解质因数
1 2 3 4 5 6 7 8 9 10 11
voiddivide(int x){ for (int i = 2; i <= x / i; i ++ ){ if (x % i == 0){ int s = 0; while (x % i == 0) x /= i, s ++ ; cout << i << ' ' << s << endl; } } if (x > 1) cout << x << ' ' << 1 << endl; cout << endl; }
voiddivide(int x){ for (int i = 2; i <= x / i; i ++ ){ if (x % i == 0){ int s = 0; while (x % i == 0) x /= i, s ++ ; cout << i << ' ' << s << endl; } } if (x > 1) cout << x << ' ' << 1 << endl; cout << endl; }
intmain(){ cin >> n; divide(n); return0; }
4.3朴素筛法求素数
1 2 3 4 5 6 7 8 9 10 11 12
int primes[N], cnt; // primes[]存储所有的素数 bool st[N]; // st[x]存储x是否被筛掉
voidget_primes(int n){ for (int i = 2; i <= n; i ++ ){ if (st[i]) continue; primes[cnt ++ ] = i; for (int j = i + i; j <= n; j += i){ st[j] = true; } } }
int n; int primes[N], cnt; // primes[]存储所有素数 bool st[N]; // st[x]存储x是否被筛掉
voidget_primes(int n){ for (int i = 2; i <= n; i ++ ){ if (!st[i]) primes[cnt ++ ] = i; for (int j = 0; primes[j] <= n / i; j ++ ){ st[primes[j] * i] = true; if (i % primes[j] == 0) break; } } }
intmain(){ cin >> n; get_primes(n); for (int i = 0; i < n; i ++ ) cout << primes[i] << endl; return0; }
4.5试除法求所有约数
1 2 3 4 5 6 7 8 9 10 11
vector<int> get_divisors(int x){ vector<int> res; for (int i = 1; i <= x / i; i ++ ){ if (x % i == 0){ res.push_back(i); if (i != x / i) res.push_back(x / i); } } sort(res.begin(), res.end()); return res; }
vector<int> get_divisors(int x){ vector<int> res; for (int i = 1; i <= x / i; i ++ ){ if (x % i == 0){ res.push_back(i); if (i != x / i) res.push_back(x / i); } } sort(res.begin(), res.end()); return res; }
intmain(){ vector<int> res; cin >> n; res = get_divisors(n); for (auto x : res) cout << x << endl; return0; }
4.6约数个数之和
4.6.1如果数N可以表示为
4.6.2约数的个数为
4.6.3约数之和为
4.7欧几里得算法
1 2 3
intgcd(int a, int b){ return b ? gcd(b, a % b) : a; }
4.8求欧拉函数
1 2 3 4 5 6 7 8 9 10 11 12
intphi(int i){ int res = x; for (int i = 2; i <= x / i ; i ++ ){ if (x % i == 0){ res = res / i * (i - 1); while (x % i == 0) x /= i; } } if (x > 1) res = res / x * (x - 1); return res; }
intphi(int x){ int res = x; for (int i = 2; i <= x / i; i ++ ){ if (x % i == 0){ res = res / i * (i - 1); while (x % i == 0) x /= i; } } if (x > 1) res = res / x * (x - 1); return res; }
intmain(){ int n; cin >> n; while (n -- ){ int a; cin >> a; int res = a; for (int i = 2; i <= a / i; i ++ ){ if (a % i == 0){ // i是a的一个质因子 res = res / i * (i - 1); while (a % i == 0) a /= i; // a就把这个质因子除干净 } } if (a > 1) res = res / a * (a - 1); // 说明a还有一个大于1的质因子 cout << res << endl; } return0; }
intmain(){ cin >> n >> m; for (int i = 1; i <= n; i ++ ) cin >> v[i] >> w[i]; for (int i = 1; i <= n; i ++ ) for (int j = 0; j <= m; j ++ ) for (int k = 0; k * v[i] <= j; k ++ ) f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]); cout << f[n][m] << endl; }
intmain(){ cin >> n >> m; for (int i = 1; i <= n; i++ ) cin >> v[i] >> w[i] >> s[i]; for (int i = 1; i <= n; i ++ ) for (int j = 0; j <= m; j ++ ) for (int k = 0; k <= s[i] && k * v[i] <= j; k ++ ) f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k * w[i]); cout << f[n][m] << endl; return0; }
int n, m; int h[N], e[M], ne[M], idx; int depth[N], fa[N][16]; int q[N];
voidadd(int a, int b){ e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ; }
voidbfs(int root){ memset(depth, 0x3f, sizeof depth); // 初始时,dist初始化为正无穷 depth[0] = 0, depth[root] = 1; // 其中0号点是哨兵,我们定义为0, 根结点是1 int hh = 0, tt = 0; q[0] = root; // 根结点先加入到队列中去 while (hh <= tt){ // 宽搜求这两个数组 int t = q[hh ++ ]; for (int i = h[t]; i != -1; i = ne[i])[ int j = e[i]; if (depth[j] > depth[t] + 1){ depth[j] = depth[t] + 1; q[ ++ tt] = j; fa[j][0] = t; for (int k = 1; k <= 15; k ++ ) fa[j][k] = fa[fa[j][k - 1]][k - 1]; } ] } }
intlca(int a, int b){ if (depth[a] > depth[b]) swap(a, b); // 如果a在b的上面,则交换 // 跳到同一层,从高往低跳 for (int k = 15; k >= 0; k -- ){ if (depth[fa[a][k]] >= depth[b]) // 跳了之后,在b的下面或者是同一层 a = fa[a][k]; // 那么说明a是可以跳的 } if (a == b) return a; // 说明a或者b就是公共祖先 // 否则同时往上跳 for (int k = 15; k >= 0; k -- ){ if (fa[a][k] != fa[a][k]){ // 说明还没有跳到公众祖先上面 a = fa[a][k]; b = fa[b][k]; } } return fa[a][0]; }
intmain(){ scanf("%d", &n); int root = 0; // 定义根结点 memset(h, -1, sizeof h); for (int i = 0; i < n; i ++ ){ int a, b; scanf("%d%d", &a, &b); if (b == -1) root = a; // b是-1,则a是根结点 elseadd(a, b), add(b, a); } bfs(root); scanf("%d", &m); while (m -- ){ int a, b; scanf("%d%d", &a, &b); int p = lca(a, b); if (p == a) puts("1"); elseif (p == b) puts("2"); elseputs("0"); } return0; }
intmain(){ scanf("%d", &n); for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]); for (int i = 1; i <= n; i ++ ) { // 空集 f[i] = 1; for (int j = 1; j < i; j ++ ) // 上升序列合法 if (a[j] < a[i]) f[i] = max(f[i], f[j] + 1); } int res = 0; for (int i = 1; i <= n; i ++ ) res = max(res, f[i]); printf("%d\n", res); return0; }
intmain(){ cin >> n; for (int i = 1; i <= n; i ++ ) cin << a[i]; int res = 0; for (int i = 1; i <= n; i ++ ) { f[i] = a[i]; for (int j = 1; j < i; j ++ ) if (a[j] < a[k]) f[i] = max(f[i], f[j] + a[i]); } for (int i = 1; i <= n; i ++ ) res = max(res, f[i]); cout << res << endl; return0; }
intmain(){ while (cin >> q[n]) n ++ ; int res = 0; for (int i = 0; i < n; i ++ ) { int res = 0; for (int j = 0; j < i; j ++ ) if (q[j] >= q[i]) f[i] = max(f[i], f[j] + 1); res = max(res, f[i]); } cout << res << endl; int cnt = 0; // 当前子序列的个数 for (int i = 0; i < n; i ++ ) { int k = 0; // 从前往后找的序列 // 只要我们没有遍历完序列 && 当前序列的结尾是小于当前数的 while (k < cnt && g[k] < q[i]) k ++ ; g[k] = q[i]; // 没有任何一个序列是大于等于当前数的 if (k >= cnt cnt ++ ; // 开一个新的序列 } cout << cnt << endl; return0; }
int n; int q[N]; int up[N], down[N]; // 分别表示上升子序列的结尾和下降子序列的结尾 int ans; // 全局最小值,使用dfs进行更新
// u 当前枚举到了第几个数, su 当前上升子序列的个数,sd 当前下降子序列的个数 voiddfs(int u, int su, int sd){ // 不可能使得答案变小了,直接返回 if (su + sd >= ans) return; // 说明找到一个方案 if (u == n) { // 更新 ans ans = su + sd; return; } // 情况1:将当前数放到上升子序列中 int k = 0; // 先进行备份 while (k < su && up[k] >= q[u]) k ++ ; int t = up[k]; up[k] = q[u]; if (k < su) dfs(u + 1, su, sd); elsedfs(u + 1, su + 1, sd); // 恢复现场 up[k] = t; // 情况2:将当前数放到下降子序列中 k = 0; while (k < sd && down[k] <= q[u]) k ++ ; t = down[k]; down[k] = q[u]; if (k < sd) dfs(u + 1, su, sd); elsedfs(u + 1, su, sd + 1); down[k] = t; }
intmain(){ while (cin >> n, n) { for (int i = 0; i < n; i ++ ) cin >> q[i]; // 进行暴搜之前,先记录最大值 n,就是最坏情况下,每个数单独一个序列 ans = n; dfs(0, 0, 0); cout << ans << endl; } return0; }
intmain(){ scanf("%d", &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 ++ ) { // 先考虑右半部分 f[i][j] = f[i - 1][j]; // 再考虑左半边,情况又细分为多种,需要依次枚举 // 当 a[i] == b[j] 的时候才会存在 if (a[i] == b[j]) { // 先考虑小块中存在的空集 f[i][j] = max(f[i][j], 1); for (int k = 1; k < j; k ++ ) { // 这里需要再判断每个小块是否存在 if (b[k] < b[j]) f[i][j] = max(f[i][j], f[i][k] + 1); } } } int res = 0; for (int i = 1; i <= n; i ++ ) res = max(res, f[n][i]); printf("%d\n", res); return0; }
intmain(){ int T; cin >> T; while (T -- ){ int a, b; string a_line, b_line; cin >> a >> b >> a_line; vector<int> number; for (auto c : a_line){ if (c >= '0' && c <= '9') number.push_back(c - '0'); if (c >= 'A' && c <= 'Z') number.push_back(c - 'A' + 10); if (c >= 'a' && c <= 'z') number.push_back(c - 'a' + 36); } reverse(number.begin(), number.end()); vector<int> res; while (number.size()){ int r = 0; for (int i = number.size() - 1; i >= 0; i -- ){ number[i] += r * a; r = number[i] % b; number[i] /= b; } res.push_back(r); while (number.size() && number.back() == 0) number.pop_back(); } reverse(res.begin(), res.end()); for (auto x : res){ if (x <= 9) b_line += char(x + '0'); if (x >= 10 && x <= 35) b_line += char(x - 10 + 'A'); if (x >= 36) b_line += char(x - 36 + 'a'); } cout << a << ' ' << a_line << endl; cout << b << ' ' << b_line << endl; cout << endl; } return0; }
2.两数之和
暴力
1 2 3 4 5 6 7 8 9 10
classSolution{ public: vector<int> twoSum(vector<int> &nums, int target){ for (int i = 0; i < nums.size() - 1; i ++ ) for (int j = i + 1; j < nums.size(); j ++ ) if (nums[i] + nums[j] == target) return {i, j}; return {}; }; };
两遍哈希
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
classSolution { public: vector<int> twoSum(vector<int> &nums, int target){ map<int, int> a; //建立hash表存放数组元素 vector<int> b(2, -1); // 存放结果 for (int i = 0; i < nums.size(); i ++ ) a.insert(map<int, int>::value_type(nums[i], i)); for (int i = 0; i < nums.size(); i ++ ){ if (a.count(target - nums[i]) > 0 && (target - nums[i] != i)){ // 判断是否找到目标元素且目标元素不能是本身 b[0] = i; b[1] = a[target - nums[i]]; break; } } return b; }; };
3.LeetCode暑期刷题打卡2019——Week1 二分专题
69.x的平方根
1 2 3 4 5 6 7 8 9 10 11 12 13
classSolution { public: intmySqrt(int x){ int l = 0, r = x; while (l < r) { int mid = l + (longlong)r + 1 >> 1; if (mid <= x / mid) l = mid; else r = mid - 1; } return r; } };
35.搜索插入位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classSolution { public: intsearchInsert(vector<int> &nums, int target){ if (nums.empty() || nums.back() < target) return nums.size(); int l = 0, r = nums.size() - 1; while (l < r) { int mid = l + r >> 1; if (nums[mid] >= target) r = mid; else l = mid + 1; } return r; } };
classSolution { public: vector<int> searchRange(vector<int> &nums, int target){ if (nums.empty()) return {-1, -1}; int l = 0, r = nums.size() - 1; while (l < r) { int mid = l + r >> 1; if (nums[mid] >= target) r = mid; else l = mid + 1; } if (nums[r] != target) return {-1, -1}; int start = l; l = 0, r = nums.size() - 1; while (l < r) { int mid = l + r + 1 >> 1; if (nums[mid] <= target) l = mid; else r = mid - 1; } int end = l; return {start, end}; } };
classSolution { public: boolsearchMatrix(vector<int> &matrix, int target){ if (matrix.empty() || matrix[0].empty()) returnfalse; int n = matrix.size(), m = matrix[0].size(); int l = 0, r = n * m - 1; while (l < r) { int mid = l + r >> 1; if (matrix[mid / m][mid % m] >= target) r = mid; else l = mid + 1; } if (matrix[r / m][r % m] != target) returnfalse; returntrue; } };
classSolution { public: intfindMin(vector<int> &nums){ int l = 0, r = nums.size() - 1; while (l < r) { int mid = l + r >> 1; if (nums[mid] <= nums.back()) r = mid; else l = mid + 1; } return nums[r]; } };
classSolution { public: intsearch(vector<int> &nums, int target){ if (nums.empty()) return-1; // 找到最小值 int l = 0, r = nums.size() - 1; while (l < r) { int mid = l + r >> 1; if (nums[mid] <= nums.back()) r = mid; else l = mid + 1; } if (target <= nums.back()) r = nums.size() - 1; else l = 0, r -- ; while (l < r) { int mid = l + r >> 1; if (nums[mid] >= target) r = mid; else l = mid + 1; } if (nums[l] == target) return l; elsereturn-1; } };
278.第一个错误的版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
boolisBadVersion(int version);
classSolution { public: intfirstBadVersion(int n){ int l = 1, r = n; while (l < r) { int mid = (longlong)l + r >> 1; if (isBadVersion(mid)) r = mid; else l = mid + 1; } return r; } };
162.寻找峰值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution { public: intfindPeakElement(vector<int>& nums){ int l = 0, r = nums.size() - 1;
while (l < r) { int mid = l + r >> 1; if (nums[mid] > nums[mid + 1]) r = mid; else l = mid + 1; }
classSolution { public: intfindDuplicate(vector<int>& nums){ int l = 1, r = nums.size() - 1; while (l < r) { int mid = l + r >> 1;
int cnt = 0; for (auto x: nums) { if (x >= l && x <= mid) cnt ++ ; }
if (cnt > mid - l + 1) r = mid; else l = mid + 1; }
return r; } };
275.H指数 II
1 2 3 4 5 6 7 8 9 10 11 12 13
classSolution { public: inthIndex(vector<int> &nums){ int l = 0, r = nums.size(); while (l < r) { int mid = l + r + 1 >> 1; if (nums[nums.size() - mid] >= mid) l = mid; else r = mid - 1; } return r; } };
19.删掉倒数第n个结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution { public: ListNode* removeNthFromEnd(ListNode *head, int n){ auto dummy = newListNode(-1); dummy->next = head; auto first = dummy, second = dummy; while (n -- ) first = first->next; while (first->next) { first = first->next; second = second->next; } second->next = second->next->next; return dummy->next; } };
classSolution { public: ListNode* rotateRight(ListNode *head, int k){ if (!head) returnNULL; int n = 0; for (auto p = head; p; p = p->next) n ++ ; k %= n; auto first = head, second = head; while (k -- ) first = first->next; while (first->next) { first = first->next; second = second->next; } first->next = head; head = second->next; second->next = NULL; return head; } };
24.交换链表中相邻的两个节点,最后返回头结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSoultion { public: ListNode * swapPairs(ListNode *head){ auto dummy = newListNode(-1); dummy->next = head; for (auto p = dummy; p->next && p->next->next;) { auto a = p->next, b = a->next; p->next = b; a->next = b->next; b->next = a; p = a; } return dummy->next; } };
206.翻转链表I
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution { public: ListNode* reverseList(ListNode *head){ if (!head) returnNULL; auto a = head, b = head->next; while (b) { auto c = b->next; b->next = a; a = b, b = c; } head->next = NULL; return a; } };
classSolution { public: ListNode* reverseBetween(ListNode *head, int m, int n){ if (m == n) return head; // 定义虚拟节点 auto dummy = newListNode(-1); dummy->next = head; // 找到b和d的位置 auto a = dummy, d = dummy; for (int i = 0; i < m - 1; i ++ ) a = a->next; for (int i = 0; i < n; i ++ ) d = d->next; // 进行翻转 auto b = a->next, c = d->next; for (auto p = b, q = b->next; q != c; ) { auto o = q->next; q->next = p; p = q, q = o; } // 改变指针 a->next = d; b->next = c; return dummy->next; } };
160.两个链表的交点
1 2 3 4 5 6 7 8 9 10 11 12 13 14
classSolution { public: ListNode *getIntersectionNode(ListNode *headA, ListNode *headB){ auto p = headA, q = headB; while (p != q) { if (p) p = p->next; else p = headB; if (q) q = q->next; else q = headA; } return p; } };
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ classSolution { public: ListNode* sortList(ListNode* head){ int n = 0; for (auto p = head; p; p = p->next) n ++ ; auto dummy = newListNode(-1); dummy->next = head;
for (int i = 1; i < n; i *= 2) { auto cur = dummy; for (int j = 0; j + i < n; j += i * 2) { auto left = cur->next, right = cur->next; for (int k = 0; k < i; k ++ ) right = right->next; int l = 0, r = 0; while (l < i && r < i && right) { if (left->val <= right->val) { cur->next = left; cur = left; left = left->next; l ++ ; }else { cur->next = right; cur = right; right = right->next; r ++ ; } } while (l < i) { cur->next = left; cur = left; left = left->next; l ++ ; } while (r < i && right) { cur->next = right; cur = right; right = right->next; r ++ ; }
// 迭代法 // 左边:左中右遍历;右边:右中左遍历,同时检查是否相等 classSolution{ public: boolisSymmetric(TreeNode *root){ if (!root) returntrue; stack<TreeNode*> left, right; auto l = root->left, r = root->right; while (l || r || left.size() || right.size()){ while (l && r){ left.push(l), l = l->left; right.push(r), r = r->right; } if (l || r) returnfalse; l = left.top(), left.pop(); r = right.top(), right.pop(); if (l->val != r->val) returnfalse; l = l->right, r = r->left; } returntrue; } };
classSolution{ public: unordered_map<int, int> pos; TreeNode *buildTree(vector<int> & preorder, vector<int> &inorder){ int n = preorder.size(); for (int i = 0; i < n; i ++ ) pos[inorder[i]] = i; returndfs(preorder, inorder, 0, n - 1, 0, n - 1); } TreeNode *dfs(vector<int> &preorder, vector<int> &inorder, int pl, int pr, int il, int ir){ if (pl > pr) returnNULL; int val = preorder[pl]; // 前序遍历的起点、终点;中序遍历的起点、终点 int k = pos[val]; // 前序遍历点在中序遍历中的位置 int len = k - il; // 左子树长度 auto root = newTreeNode(val); root->left = dfs(preorder, inorder, pl + 1, pl + len, il, k - 1); root->right = dfs(preorder, inorder, pl + len + 1, pr, k + 1, ir); return root; } };
classSolution { public: vector<vector<int>> levelOrder(TreeNode *root){ vector<vector<int>> res; if (!root) return res; queue<TreeNode*> q; q.push(root); while (q.size()){ int n = q.size(); // 当前这一层点的个数 vector<int> level; //当前遍历的结果 for (int i = 0; i < len; i ++ ){ auto t = q.front(); q.pop(); level.push_back(t->val); if (t->left) q.push(t->left); if (t->right) q.push(t->right); } res.push_back(level); } return res; } };
236.求两个点的最近公共祖先
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
classSolution{ public: TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q){ // 如果以root为根的子树中包含q和q,则返回他们的最近公共祖先 // 如果只包含p, 则返回p // 如果只包含q, 则返回q // 如果都不包含,则返回NULL if (!root || root == p || root == q) return root; auto left = lowestCommonAncestor(root->left, p, q); auto right = lowestCommonAncestor(root->right, p, q); if (!left) return right; if (!right) return left; return root; } };
543.二叉树的直径
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
classSolution{ public: int ans = 0; intdiameterOfBinaryTree(TreeNode *root){ dfs(root); return ans; } intdfs(TreeNode *root){ if (!root) return0; auto left = dfs(root->left); auto right = dfs(root->right); ans = max(ans, left + right); returnmax(left + 1, right + 1); } };
classSolution{ public: intcompareVersion(string s1, string s2){ // 定义两个指针,分别从0开始 int i = 0, j = 0; // 两个指针只要有一个不空,就一直做 while (i < s1.size() || j < s2.size()){ int x = i, y = j; while (x < s1.size() && s1[x] != '.') x ++ ; while (y < s2.size() && s2[y] != '.') y ++ ; // 特判一些,可能为0 int a = i == x ? 0 : atoi(s1.substr(i, x - i).c_str()); int b = j == y ? 0 : atoi(s2.substr(j, y - j).c_str()); // 返回答案 if (a > b) return1; if (a < b) return-1; // 跳过'.' i = x + 1, j = y + 1; } return0; } };
929.Unique Email Addresses
先处理邮箱字符串
后使用哈希表数出不同的邮箱的种类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classSolution{ public: intnumUniqueEmails(vector<string>& emails){ unordered_set<string> hash; for (auto email : emails){ int at = email.find('@'); string name; for (auto c : email.substr(0, at)); if (c == '+') break; elseif (c != '.') name += c; string doumain = email.substr(at + 1); hash.insert(name + '@' + doumain); } return hash.size(); } };
classSolution{ public: string longestPalindrome(string s){ string res; // 遍历字符串 for (int i = 0; i < s.size(); i ++ ){ int j, k; // j 往左走,k 往右走 // 回文串长度是奇数 for (j = i, k = i; j >= 0 && k < s.size() && s[j] == s[k]; j --, k ++ ){ if (res.size() < k - j + 1) res = s.substr(j, k - j + 1); } // 回文串长度是偶数 for (j = i, k = i + 1; j >= 0 && k < s.size() && s[j] == s[k]; j --, k ++ ){ if (res.size() < k - j + 1) res = s.substr(j, k - j + 1); } return res; } } };
classTrie { public: // Tire树节点一般用结构体表示 structNode { // 有没有以这个节点为结尾的单词 bool is_end; // 最多26个儿子 Node *son[26]; // 初始化 Node() { is_end = false; for (int i = 0; i < 26; i ++ ) son[i] = NULL; } }*root; /** Initialize your data structure here. */ Trie() { // 先创建根节点 root = newNode(); } /** Inserts a word into the trie*/ voidinsert(string word){ // 就从根节点开始遍历 auto p = root; // 从前往后遍历每一个字母 for (auto c : word) { // 首先存储这个字母的编号 int u = c - 'a'; // 判断儿子是否存在,不存在就创建 if (p->son[u] == NULL) p->son[u] = newNode(); // p走到儿子上去 p = p->son[u]; } // 最后标记一下,现在有了以p结尾的单词 p->is_end = true; } // boolsearch(string word){ // 查询也是一样,从前往后遍历这个单词 auto p = root; for (auto c : word) { // 求一下当前的编号 int u = c - 'a'; // 如果当前儿子不存在的话,说明当前路径不存在,也就是说该单词不存在 if (p->son[u] == NULL) returnfalse; // 否则p继续向下走即可 p = p->son[u]; } // 最后进行判断是否有单词结束标记 return p->is_end; } /** Returns if there is any word in the trie that starts with the given prefix*/ boolstartWith(string prefix){ // 查询也是一样,从前往后遍历这个单词 auto p = root; for (auto c : prefix) { // 求一下当前的编号 int u = c - 'a'; // 如果当前儿子不存在的话,说明当前路径不存在,也就是说该单词不存在 if (p->son[u] == NULL) returnfalse; // 否则p继续向下走即可 p = p->son[u]; } // 找前缀,直接返回即可 returntrue; } };
classSolution { public: int n, m; // 矩阵的长和宽 int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; boolexist(vector<<char>>& board, string word){ // 如果矩阵是空的,或者行是空的,直接返回 if (board.empty() || board[0].empty()) returnfalse; n = board.size(), m = board[0].size(); for (int i = 0; i < n; i ++ ) for (int j = 0; j < m; j ++ ) if (dfs(board, i, j, word, 0)) returntrue; returnfalse; } // x, y 表示矩阵中的位置,u 表示单词中的位置 booldfs(vector<vector<char>>& board, int x, int y, string& word, int u){ // 如果当前矩阵的位置上字符不等于单词的字符 if (board[x][y] != word[u]) returnfalse; // 如果全部匹配了,直接返回 if (u == word.size() - 1) returntrue; // 否则,当前这个 xy 位置已经使用了,需要标记,后续不再使用 board[x][y] = '.'; // 枚举上下左右四个方向 for (int i = 0; i < 4; i ++ ) { int a = x + dx[i], b = y + dy[i]; // 判断是否在界内 if (a >= 0 && a < n && b >= 0 && b < m) { // 如果走到下一个格子,可以匹配的话 if (dfs(board, a, b, word, u + 1)) returntrue; } } // 恢复现场 board[x][y] = word[u]; // 否则,最后都没有匹配成功的话,就直接返回 returnfalse; } };
classSolution { public: vector<vector<int>> ans; vector<int> path; vector<vector<int>> subsetsWithDup(vector<int> & nums) { // 为了把相同数字放在一起,先排序 sort(nums.begin(), nums.end()); dfs(nums, 0); return ans; } voiddfs(vector<int>& nums, int u){ if (u == nums.size()) { ans.push_back(path); return; } // 计算当前数字的个数 int k = 0; while (u + k < nums.size() && nums[u + k] == nums[u]) k ++ ; // 每次从 0 枚举到 k for (int i = 0; i <= k; i ++ ) { dfs(nums, u + k); path.push_back(nums[u]); } // 恢复现场 for (int i = 0; i <= k; i ++ ) path.pop_back(); } };
classSolution { public: vector<vector<int>> ans; vector<int> path; vector<vector<int>> combinationSum3(int k, int n) { dfs(k, 1, n); return ans; } voiddfs(int k, int start, int n){ // 如果当前枚举完了所有数的话 if (!k) { // 再判断当前的和是否是 n if (!n) return ans.push_back(path); return; } // 从 start 位置开始往后枚举 i <= 10 - k for (int i = start; i <= 9; i ++ ) { // 先把 i 加进来 path.push_back(i); // 枚举下一个数,是倒着枚举,从 i + 1 位置开始找,总和就是 n - i dfs(k - 1, i + 1, n - i); // 恢复现场 path.pop_back(); } } };