1657 字
8 分钟
算法竞赛入门
2025-12-12
统计加载中...

首先是我的模板#

#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 second
const 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 = 0
    • a ^ 0 = a
    • a ^ 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 + n
void 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]}
算法竞赛入门
https://www.nanye404.top/posts/算法整合/
作者
南叶酱
发布于
2025-12-12
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
示例歌曲
示例艺术家
封面
示例歌曲
示例艺术家
0:00 / 0:00