基础算法
枚举:框定一个范围,遍历其中的所有东西.
搜索:从一个初始状态出发,一步一步走到相邻的状态
枚举优化:1.改变枚举对象.
2.倒叙枚举.
3.减少枚举量.
题1:颜色统计
#include <iostream>using namespace std;
const int maxn = 2e5 + 10;int c[maxn];
int vis[maxn];
int pre[maxn];int main()
{int n;cin >> n;for (int i = 1; i <= n; ++i){cin >> c[i];pre[c[i]] = vis[i];vis[c[i]] = i;}long long ans = 0;for (int i = 1; i <= n; ++i){ans += (i - pre[i]) * (n - i + 1);}cout << ans << '\n';return 0;
}
题2: 取模
#include <iostream>
#include <algorithm>using namespace std;
const int maxn = 2e5 + 10;int tong[(int)(1e8 + 3)];
int a[maxn];
int b[maxn];int main()
{int n;cin >> n;int maxx = -1;for (int i = 1; i <= n; ++i){cin >> a[i];tong[a[i]]++;maxx = max(maxx, a[i]);}int len = 0;for (int i = 1; i <= maxx; ++i){int cnt = 0;while (tong[i] > 0 && cnt <= 2){cnt++;a[++len] = i;tong[i]--;}}n = len;sort(a + 1, a + n + 1);int ans = -1;for (int k = n; k >= 1; --k){if (ans >= a[k])break;for (int i = 1; i <= n; ++i){b[i] = a[i] % a[k];}sort(b + 1, b + n + 1);for (int i = 1; i <= n; ++i){b[i] = b[i + 1];}ans = max(ans, b[n - 1] + b[n - 2] - a[k]);for (int i = 1, j = n - 1; j >= i; ++i){while (b[i] >= a[k] - b[j] && j >= i){j--;}ans = max(ans, b[i] + b[j]);}}cout << ans << '\n';return 0;
}
题3: 讨论
DFS:一条路走到黑,回溯(好写,层数深会T).
BFS:同一层的位置同时遍历到,队列维护,求最优解(可行性与最优性问题,同一层的数据多会M).
题4: 生日蛋糕
#include <iostream>using namespace std;
typedef long long ll;
const int N = 2e4 + 10;
const int M = 30;int n, m;
ll ans = 1e18 + 7;
ll minv[M], mins[M];void dfs(int idx, ll nows, ll nowv, int lstr, int lsth)
{if (idx == 0){if (nowv == n)ans = min(ans, nows);return;}if (nows + mins[idx] > ans)return;if (nowv + minv[idx] > n)return;ll maxv = 0;for (int i = 1; i <= idx; ++i)maxv += (lstr - i) * (lstr - i) * (lsth - i);if (nowv + maxv < n)return;for (int i = idx; i < lstr; ++i){for (int j = idx; j < lsth; ++j){dfs(idx - 1, nows + 2 * i * j, nowv + i * i * j, i, j);}}
}int main()
{cin >> n >> m;for (int i = 1; i <= m; ++i){minv[i] = minv[i - 1] + i * i * i;mins[i] = mins[i - 1] + 2 * i * i;}for (int i = m; i * i <= n; ++i){for (int j = m; j <= n / i / i; ++j){dfs(m - 1, i * i + 2 * i * j, i * i * j, i, j);}}if (ans == (ll)(1e18 + 7))cout << 0 << '\n';elsecout << ans << '\n';return 0;
}
剪枝:为什么大佬想到了我这个蒟蒻没想到呢?菜就多练.一些明显会使接下来不合法的策略可以舍弃.
题5: 滑雪
#include <iostream>
#include <cstring>using namespace std;
const int maxn = 110;int n, m;
int dx[maxn] = {-1, 1, 0, 0};
int dy[maxn] = {0, 0, 1, -1};
int a[maxn][maxn];
int f[maxn][maxn];int dfs(int x, int y)
{if (f[x][y])return f[x][y];f[x][y] = 1;for (int i = 0; i <= 3; ++i){if (x + dx[i] >= 1 && x + dx[i] <= n && y + dy[i] >= 1 && y + dy[i] <= m)if (a[x][y] > a[x + dx[i]][y + dy[i]])f[x][y] = max(dfs(x + dx[i], y + dy[i]) + 1, f[x][y]);}return f[x][y];
}int main()
{cin >> n >> m;for (int i = 1; i <= n; ++i){for (int j = 1; j <= m; ++j){cin >> a[i][j];}}int ans = 0;for (int i = 1; i <= n; ++i){for (int j = 1; j <= m; ++j){ans = max(ans, dfs(i, j));}}cout << ans << '\n';return 0;
}
\(\text{A}^*\) 启发式搜索,基于 \(Dijstra\) 的贪心:用一个 \(g\) 表示已经获得的价值,用一个 \(h\) 表示从当前状态走到最终状态的理论最优价值.令 \(f = g + h\) 那么 \(f\) 是估价函数(估计的总价值),我们向 \(f\) 最小的地方走,如果 \(f > 当前的 ans\) ,就回头;如果有多条路径可以走,那么我们走 \(f\) 最小的.当 \(h = 0\) 时,这就是个 \(dijstra\) ,当边权为 \(0\) ,这就是个 \(dfs\) .
记忆化搜索:我们开一个数组 \(res[u]\) ,记录u这个状态出发到达最终状态的代价.那么我们走到每个点的次数就会大大减少了
双向广搜:从起点和终点同时或分别进行广搜,检查是否到达了相同的点,与折半搜索类似.
枚举子集:用 \(for\) 循环替代 \(dfs\) ,如果我们要枚举一个集合的所有子集,那我们可以把这个子集看作一个长度为全集大小的二进制串(一个位置选或不选). \((s >> 1) \& 1\) 取出这个二进制串从小到大的第 \(i(0 \le i < n)\) 位.
for (int S_ = S; S_; S_ = (S_ - 1) & S) //枚举了所有 S_ 包含于 S .
复杂度: \(O(3^n)\)
求所有子集的数的和的异或和.
\(dfs\) 或:
for (int i = 1; i <= 25; ++i)
{mem[1 << i] = i;
}
for (int i = 0; i < (1 << n); ++i)
{f[i] = f[i ^ lowbit(i)] + a[mem[lowbit(i)]];
}
归并排序: \(O(n\log n)\)
逆序对: 归并排序来求.
序列分治: 就像归并排序一样的思路:
1.合并:将分治两边处理完之后,在本层合并,有时候就是简单相加.
2.统计:统计完分支两边的答案,统计跨过 \(mid\) 的答案.
二维偏序:所有满足 \(a_i < a_j,b_i > b_j\) 的点对的数量,将 \(a_i\) 作为下表就行.
三维偏序:用树状数组.
题6:平面最近点对(加强版)
题7:Pudding Monsters
贪心:为什么大佬想到了我这个蒟蒻没想到呢?菜就多练.不断地找当前的最优解,最后发现刚好是全局最优解.
1.找到贪心算法.
2.验证它的正确性.
题8:SAM-Toy Cars
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>using namespace std;
const int P = 5e5 + 10;struct Node
{int id, num;friend bool operator < (const Node &a, const Node &b){return a.num < b.num;}
};bool vis[P];
int b[P];
int a[P];
int nxt[P];
priority_queue <Node> q;int main()
{int n, k, p;cin >> n >> k >> p;for (int i = 1; i <= p; ++i){cin >> a[i];nxt[b[a[i]]] = i;b[a[i]] = i;}for (int i = 1; i <= p; ++i)if(!nxt[i])nxt[i] = 0x3f3f3f3f;int ans = 0;for (int i = 1; i <= p; ++i){if (vis[a[i]]){++k;q.push((Node){i, nxt[i]});continue;}if (q.size() < k){++ans;q.push((Node){i, nxt[i]});vis[a[i]] = true;continue;}Node now = q.top();q.pop();++ans;vis[a[now.id]] = false;vis[a[i]] = true;q.push((Node){i, nxt[i]});}cout << ans << '\n';return 0;
}
二分: \(l,mid,r\) 的艺术.
题9:导弹拦截
题10:Max Median
#include <iostream>using namespace std;
const int N = 1e6 + 10;
const int M = 1e6 + 10;int n, k;
int a[N];
int d[M], s[M], t[M];
int cf[M];
int sum[M];bool check(int mid)
{sum[0] = 0;for (int i = 1; i <= n; ++i){sum[i] = sum[i - 1] + (a[i] >= mid);}int mn = 1e9;for (int l = 0, r = k; r <= n; ++r){while (r - l >= k){mn = min(mn, 2 * sum[l] - l);++l;}if (2 * sum[r] - r > mn){return true;}}return false;
}int main()
{cin >> n >> k;for (int i = 1; i <= n; ++i){cin >> a[i];}int l = 0, r = n, mid, ans = 0;while (l <= r){mid = (l + r) >> 1;if (check(mid)){ans = mid;l = mid + 1;}else{r = mid - 1;}}cout << ans << '\n';return 0;
}
题10:借教室
#include <iostream>
#include <cstring>
#define int long longusing namespace std;
const int N = 1e6 + 10;
const int M = 1e6 + 10;int n, m;
int r[N];
int d[M], s[M], t[M];
int need[M];
int cf[M];bool check(int mid)
{memset(cf, 0, sizeof(cf));for (int i = 1; i <= mid; ++i){cf[s[i]] += d[i];cf[t[i] + 1] -= d[i];}for (int i = 1; i <= n; ++i){need[i] = need[i - 1] + cf[i];if (need[i] > r[i]){return false;}}return true;
}signed main()
{cin >> n >> m;for (int i = 1; i <= n; ++i){cin >> r[i];}for (int i = 1; i <= m; ++i){cin >> d[i] >> s[i] >> t[i];}int l = 1, r = m, mid, ans = -1;while (l <= r){mid = (l + r) >> 1;if (check(mid)){l = mid + 1;}else{ans = mid;r = mid - 1;}}if (ans == -1){cout << 0 << '\n';}else{cout << -1 << '\n';cout << ans << '\n';}return 0;
}