1657 字
8 分钟
算法竞赛入门
首先是我的模板
#include<bits/stdc++.h>using namespace std;#define int long long#define endl '\n'#define pb push_back#define rz resize#define pll pair<int, int>#define fi first#define se secondconst int mod = 998244353;const int inf = 0x3f3f3f3f3f3f3f3f;const int N = 1e6 + 10;//常用数据结构大小,根据题目修改int dx[4] = {-1, 0, 1, 0};int dy[4] = {0, 1, 0, -1};void solve(){
}signed main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int _ = 1; //cin >> _; while (_--) solve(); return 0;}- 大概就是这样,用作参考
- 基本的输入输出我就不讲了
stl:string
- 只写一些常用的
string t = s.substr(l, r - l);//获取字串,左闭右开,索引从0开始auto p = s.find("abc");auto q = s.rfind('x');if (pos != string::npos) { // 找到了} else { // 没找到}s.insert(pos, "ins"); // 在 pos 前插入s.erase(pos, len); // 删 [pos, pos+len)s.erase(s.begin()+l, s.begin()+r); // 删区间 [l, r)- 还有一些桶计数,用scall码计算即可
位运算
&与 同1为1- ` 或 有1为1
^异或(最常用)不同为1<<左移 乘2^k>>右移 除2^k
- 常用的性质
- 判断奇偶
bool odd = (x & 1);最快的方法,因为是直接二进制计算(不过也不会用到) a ^ a = 0a ^ 0 = aa ^ b = b ^ a(a ^ b) ^ c = a ^ (b ^ c)a ^ b ^ a = b
- 判断奇偶
- 应用
- 找出只出现一次的数,其他都是偶数次
for (int i = 1; i <= n; i++)ans ^= a[i];- nim游戏
- 异或和为0先手必败,否则必胜
- 开关灯问题(翻转问题)
- 前缀异或
dfs
排列
void dfs(int x){ if(x == n + 1){ for(int i = 1; i <= n; i++) { cout <<setw(5)<< arr[i] << " "; } cout << endl;}
for(int i = 1; i <= n; i++) { if(!st[i]) { st[i] = true; arr[x] = i; dfs(x + 1); st[i] = false; arr[x] = 0; } }}组合
void dfs(int x,int start){ if(x > r) { for(int i = 1; i <= r; i++) { cout << setw(3) << arr[i]; } cout << endl; return; } for(int i = start; i <= n; i++) { arr[x] = i; dfs(x + 1,i + 1); arr[x] = 0; }}模板
void dfs(int x){ 递归出口;
剪枝函数;
向后搜索() { 修改;
递归调用 dfs(x + 1);
恢复; }}bfs
- 用队列实现
- 八皇后
//c是撇,和为x + i d是捺,x - i + nvoid dfs(int x){ if(x > n) { cnt ++; if(cnt <= 3) { for(int i = 1; i <= n; i++) cout << a[i] <<" "; cout << endl; } return; } for(int i = 1; i <= n; i++) { if(b[i] || c[i + x] || d[x - i + n]) continue; else { a[x] = i; b[i] = 1;c[i + x] = 1; d[x - i + n] = 1; dfs(x + 1); b[i] = 0;c[i + x] = 0; d[x - i + n] = 0; a[x] = 0; } }}模板
void bfs(){ vis[1] = 0; q.push(1); while(!q.empty()) { int x = q.fornt();q.pop; //x出 for(int y : e[x]){ if(vis[y]) continue; vis[y] = 1; //y入 q.push(y); } }}二分
二分搜索
- 直接用stl
//大于等于x的第一个数int k = *lower_bound(a.begin() + 1, a.end(), x);//获取值//大于x的第一个int pos = upper_bound(v.begin(), v.end(), x) - v.begin();//获取下标二分答案
- 答案具有单调性就可以用
//二分答案,用于判断int l = 0, r = 2e9;//取定二分左右边界while(l + 1 != r)//终止条件,这个很重要,我一般是这么写{int mid = l + r >> 1;//取中点值(本质是向下取整) if(check(mid))//分类判断mid和标准答案的区别 l = mid; else r = mid;}return l;//返回左边界,注意左边界是答案,不同需求下可能是 r- 分为最大化答案和最小化答案两种
- 可行区在左边,找最大化答案,返回 l
- //可行区在右边,找最小化答案,返回 r
优先队列
//优先队列/堆priority_queue <int, vector<int> , less<int> > pq; //大顶堆priority_queue <int, vector<int>, greater<int> > mpq; //小顶堆pq.size(); //元素个数pq.empty(); //是否为空pq.push(x); //插入元素pq.pop(); //删除堆顶元素pq.top(); //访问堆顶元素int a[5] = {4,1,3,5,2};for(int i = 0; i < 5; i++) pq.push(a[i]);while(!pq.empty()){ cout << pq.top() << " "; //输出堆顶元素 p.pop(); //删除堆顶元素}for(int i = 0; i < 5; i++) mpq.push(a[i]);while(!mpq.empty()){ cout << mpq.top() << " "; //输出堆顶元素 mpq.pop(); //删除堆顶元素}哈希
map<string, int> mp; // 初始化mp["apple"] = 5; // 插入或修改元素mp.insert({"banana", 3}); // 插入元素mp.erase("apple"); // 删除元素auto it = mp.find("banana"); // 查找元素,返回迭代器int cnt = mp.count("banana"); // 返回键数量(0或1)int sz = mp.size(); // 返回映射大小bool empty = mp.empty(); // 判断是否为空mp.clear(); // 清空映射int value = mp["banana"]; // 访问键对应的值// 遍历映射for (auto it = mp.begin(); it != mp.end(); ++it){ string key = it->first; int value = it->second;}// 使用范围for循环遍历for (auto &pair : mp){ string key = pair.first; int value = pair.second;}动态规划
- 简单的dp就是递推,状态转移方程很容易看出来,比如斐波那契
- 关键是找到合适的f(x)
- 就比如区间问题常见的就是选取以x结尾的f(x)
背包问题
简化的01背包
- 背包的容积为
v,有n个物品和v[i]代表每个物品的体积 - 要求最小的剩余体积
- 首先最先想到的应该都是贪心,先选最大的,然后依次选,但是也很容易证明贪心是错的
- 比如容积为5,物品为4,3,2
- 不难看出暴力的做法就是dfs,每个物品都有选和不选两种,一个指数型的dfs
- 但是重点是我们并不关心这么去选,而是关心剩余的体积(或者已经选了的体积总和)
- 因此开一个
f[i][j]表示前i个是否能放满体积为j的背包,f[i][j] = f[j - 1][j] || f[i - 1][j - v[i]],后者是放i,前者是不放i
01背包
- n件物品,第i件的容量是
v[i],价值w[i],哪些物品装入让总价值最大 - 根据前面的思路,
f[i][j]为前i件物品放入体积为j背包的最大价值f[i][j] = max{f[i - 1][j] , f[i - 1][j -v[i]] + w[i]}
扩展
- 如果背包的体积太大了开不下了,怎么办?
- 如果n比较小,直接dfs
- 如果n再大一点,用整体二分把两份的答案拼在一起
完全背包
- 在01背包的基础上所有物品可以无限放
- 思路和上面一样,就是要修改一下状态转移方程,
f[i][j] = max{f[i - 1][j] , f[i - 1][j - k * v[i]] + k * w[i]},此时0 <= k * v[i] <= j - 但是这样的时间复杂度又太高了,那我们不妨换个思路,还是用01背包的处理方式,但是可以这么写
f[i][j] = max{f[i - 1][j] , f[i][j - v[i]] + w[i]}
部分信息可能已经过时