- 冯之几 的博客
代码模板【自存】
- @ 2026-7-18 22:37:12
自存,无不良用途,请赵老师明辨是非!——冯之几,2026-07-18, 22:35
语法阶段:
//判断回文数
bool isHWS(int num) {
// 方法1
// int temp=num,ans=0;
// while (temp!=0) {
// ans=ans*10+temp%10;
// temp/=10;
// }
// if (ans==num)
// return true;
// else
// return false;
//方法2
string a = to_string(num);
string b = a;
reverse(a.begin(), a.end());
return a == b;
}
// 试除法判断质数
int isPrime(int n) {
if (n < 2) return 0;//质数必须是大于1的整数
for (int i = 2; i <= n / i ; i++) { // 试除范围 [2,sqrt(n)] (靴子定理)
if (n % i == 0) //找到因子 不是质数
return 0;
}
return 1; // 循环过程中没找到因子 是质数
}
// 辗转相除法 gcd greatest common divisor 2最大公约数(a大b小)
// gcd(a, b) == gcd(b, a%b)
int gcd(int a, int b){
return b == 0? a : gcd(b, a % b);
}
// 分解质因数
/*
代码作用: 对一个数字进行质因数分解 从小到大输出所有因子
例如 输入20
输出 2 2 5
*/
void divide(int n) {
// 从2开始,试除每个数
for (int i = 2; i <= n / i; i++) { // 枚举 2 ~ sqrt(n)
// 如果n能被i整除,说明i是一个质因数
while (n % i == 0) {
cout << i << " ";
n /= i; // 用i整除n,继续分解
}
}
if (n != 1) // 大于 sqrt(n) 的因子最多有一个 单独 处理
cout << n;
}
// 埃氏筛法 O(n * loglog n) C * N
const int N = 1e7 + 10;
int prime[N], cnt, n, a;
bool st[N]={1,1};//标记有没有被筛掉
//埃式筛法-O(nloglogn) 只筛质数的倍数
void get_primes(int n) {
for(int i = 2; i <= n; i++) { // n / lnn * n^0.5
if(st[i]==0){ //i是质数
for(int j = i; j <= n / i; j ++) // 筛掉i的倍数 倍数大于等于 i
st[i * j] = true;//筛掉 i*j 标记下
}
}
}
//线性筛法 -O(n), n = 1e7的时候基本就比埃式筛法快一倍了
//算法核心:数字仅会被其最小质因子筛去 56
const int N = 1e7 + 10;
int prime[N], cnt;
bool st[N];//st[i]=0 没被筛掉 st[i]=1被筛掉
void get_prime(int x) {
for (int i = 2; i <= x; i++) {
if (st[i] == 0) prime[cnt++] = i; //prime[]存储1-i范围的质数
for (int j = 0; prime[j] * i <= x; j++) {
//对于任意一个合数x,假设pj为x最小质因子,当i<x/pj时,一定会被筛掉
st[prime[j]*i] = true;
if (i % prime[j] == 0)
break; //保证pj是最小质因子核心
}
}
}
算法入门:
//冒泡排序 o(n^2) 稳定
/*
排序思路
1. 比较相邻的元素。如果顺序不对,就交换。
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样就完成了一轮冒泡排序,
3. 进行 i 轮冒泡排序后 可以确定后i个元素位置是正确的。 所以我们需要进行n-1轮冒泡排序。
4. 如果一趟排序没产生交换 说明排好了 结束排序
*/
#include<iostream>
using namespace std;
void BubbleSort(int a[], int n) {
int swapped;//1 标记:一趟排序有没有产生交换
for (int i = 0; i < n - 1; i++) {
swapped = 0; //2 每趟排序开始前都假设 没产生交换
for (int j = 0; j < n - 1 - i; j++) { //排序区间可以越来越短
if (a[j] > a[j + 1]) {
swap(a[j], a[j + 1]);
swapped = 1; //3 产生交换 标记值改成1
}
}
if (swapped == 0) { //4 如果一趟排序没产生交换 说明排好了 结束排序
break;
}
}
}
//插入排序 o(n^2) 稳定
/*
插入排序 o(n2)
1.从第二个数字开始 把每个插入到前面合适的位置
插入过程:
1.用temp存储待排数字
2.循环 前面比temp大的都往后拉
3.temp插入到j位置
*/
int insertSort( int a[], int size) {
for (int i = 1; i < size ; i++) {
int temp = a[i], j = i; //存储待排数字的值和下标
while (j > 0 && a[j - 1] > temp ) { //前面比temp大的都往后拉
a[j] = a[j - 1]; // 赋值(向后拉)
j--;
}
a[j] = temp; //插入
}
}
// 选择排序 o(n^2) 不稳定
/*
不稳定性证明
例如:
5* 2 3 5 1
1 2 3 5 5*
排序思想:
1. 从数组的第一个位置开始,将其视为已排序区间的唯一元素。
2. 遍历未排序区间,找到最小(或最大)的元素。
3. 将找到的最小(或最大)元素与未排序区间的第一个元素进行交换。
4. 将交换后的元素作为已排序区间的最后一个元素。
5. 重复步骤2至4,直到所有元素都被排序。
每次选出 索引:i-n 范围内最小的数字 把它放到第i个位置
*/
#include <iostream>
using namespace std;
void selectionSort(int arr[], int n) {
for (int i = 0; i < n - 1; i++) {
int minIndex = i;//假设最小的数字索引就是i
for (int j = i + 1; j < n; j++) { //找最小的数字的位置
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
swap(arr[i], arr[minIndex]);
}
}
// 快速排序
#include<iostream>
using namespace std;
const int N=1e5+10;
int a[N],n;
void QuickSort(int s[],int l,int r)
{
if(l<r)//区间内数字个数 > 1
{
swap(s[l],s[(l+r)/2]);//优化 防止顺序数字超时
int i=l,j=r,x=s[l];//x为基准
while(i<j)//左右指针不相遇
{
while(i<j&&s[j]>x)//从右往左扫描 位置不对的元素
j--;
if(i<j)//异常元素抛到左边 左指针右移
swap(s[i++],s[j]);
while(i<j&&s[i]<x)
i++;
if(i<j)
swap(s[j--],s[i]);
}
QuickSort(s,l,i-1);
QuickSort(s,i+1,r);
}
}
int main()
{
cin >> n;
for(int i = 0;i < n; i++)
{
cin >> a[i];
}
QuickSort(a,0,n-1);
for (int i = 0;i < n;i++)
{
cout<<a[i]<<" ";
}
return 0;
}
//计数排序 (每个数字都有专属的桶) o(n+M) 简易版 不要求稳定性时用
/*
解决 数字分布范围不大的(三千万以内) 的 非负整数排序问题 去重很方便 空间换时间
数字分布范围为 0-n时:有n+1个桶
*/
#include<iostream>
using namespace std;
const int M=100000;//M代表数字最大值
int main(){
int a[M+10]={0};
int n,k;
cin>>n;//输入n个数字
for(int i=0;i<n;i++)
{
cin>>k;
a[k]++;//给k号桶投一票
}
for(int i=0;i<=M;i++)//遍历所有的桶
{
int k=0;
while(k<a[i])//完整排序结果
{
cout<<i<<" ";
k++;
}
// if(a[i]!=0)//去重排序结果
// {
// cout<<i<<" ";
// }
}
return 0;
}
//计数排序 稳定性
const int N = 100010;
const int W = 100010;
int n, w, a[N], cnt[W], b[N];
void counting_sort() {
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; ++i) ++cnt[a[i]];
for (int i = 1; i <= w; ++i) cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; --i) b[cnt[a[i]]--] = a[i];
}
// 二分
//二分查找 查找的数字唯一 或 不存在
int binarySearch(int a[],int l,int r,int x){
while(l<=r){ // 注意等号
int mid=(l+r)/2;//取中间数字的下标
if(a[mid]==x){
return mid;
}else if(a[mid]<x){
l=mid+1;
}else{
r=mid-1;
}
}
return -1;
}
int bs_l(int a[],int l,int r,int n)//左边界二分 返回的是第一个等于 或 第一个大于目标数字的下标
{
while(l<r)
{
int mid=(l+r)/2; //l=2 r=3 希望mid向下取整 不用+1
if(a[mid]>=n)
r=mid;
else
l=mid+1;
}
return l;
}
int bs_r(int a[],int l,int r,int n)//右边界二分 返回的是最后一个等于 或 最后一个小于 目标数字的下标
{
while(l<r)
{
int mid=(l+r+1)/2; //l=2 r=3 希望mid向上取整 要+1
if(a[mid]<=n)
l=mid;
else
r=mid-1;
}
return l;
}
//高精度加
string add(string a, string b) {
int la = a.size(), lb = b.size(), f = 0;//la a剩余的需要加的位数 f进位标记
string ans;
while (la || lb || f) { //数字没加完 或者有进位 就循环
int t = (la>0?a[--la]-'0':0) + (lb>0?b[--lb]-'0':0) + f;//取对应位置数据 相加
f = t / 10;
t %= 10;
ans = ans+(char)(t + '0');//内存
}
reverse(ans.begin(),ans.end());
return ans;
}
//dfs 深搜基本模板:
int check(参数)//约束条件判断 有没有路
{
if(满足条件)
return 1;
return 0;
}
void dfs(int step)//step:深度
{
判断边界 //找到迷宫出口/思路
{
相应操作//结束任务
}
for()//循环逼历每一种情况 类似前左右这样的遍历规则
{
if(check);//满足check条件 检查有没有路
标记该节点访问过
dfs(step+1);递归继续下一步
节点恢复初始状态(回溯)
}
}
// 全排列问题
#include <iostream>
#include <iomanip>
using namespace std;
int a[100]={0},b[100]={0},n;
void dfs(int dep){ //深度代表第几个数字
if(dep==n+1){
for(int i=1;i<=n;i++){
cout<<setw(5)<<a[i];
}
cout<<endl;
return;
}
else{
for(int i=1;i<=n;i++){ //尝试所有的数字
if(b[i]==0){ //b[i]==0 表示i没用过 b[i]==1 表示i用过
a[dep]=i;
b[i]=1;//使用过的数字标记为1
dfs(dep+1);
b[i]=0;//标记 重置方便回溯
}
}
}
}
int main(){
cin>>n;
dfs(1);
return 0;
}
//bfs 迷宫
void bfs()
{
node p;
p.x=sx; //存储一个起点
p.y=sy;
queue<node>q;//辅助队列
q.push({});//避免异常
d[sx][sy]=0;
while(!q.empty())
{
node tmp=q.front();//取队头
q.pop();
for(int i=0;i<4;i++)//向四个方向扩散
{
int xx=tmp.x+dx[i];
int yy=tmp.y+dy[i];
//如果新节点可以到达 且 在棋盘内 且没到过 更新步数 入队
if(maze[xx][yy]!='#' && xx>0&&yy>0&&xx<=n&&yy<=m && d[xx][yy]<0)
{
node tp;
tp.x=xx;
tp.y=yy;
d[xx][yy]=d[tmp.x][tmp.y]+1;//更新距离
q.push(tp); //存储新到达的点
}
}
}
}
// lis 最长上升子序列(模板题)
/*
解题思路:DP O(n2)
状态表示:f[i]表示 以w[i]结尾的 上升子序列 的最大长度。
状态转移:f[i] = max(f[i], f[j] + 1)。j∈(0,1,2,..,i-1),且满足 w[i] > w[j]
f[i]最小值为1(自己单独构成一个子序列)
*/
#include <iostream>
using namespace std;
const int N = 1010;
int n;
int w[N], f[N];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> w[i];
int ml = 1; // 存储所有 f[i]之中的最大值
for (int i = 1; i <= n; i++) {
f[i] = 1; // f[i]默认为1 自己开一个新序列 长度至少是
for (int j = 1; j < i; j++) {
if (w[i] > w[j]) f[i] = max(f[i], f[j] + 1); // 前一个小于自己的数结尾的最大上升子序列加上自己,即+1
}
ml = max(ml, f[i]);//记录最大长度ml
}
cout << ml << endl;
return 0;
}
// 最长公共子序列 lcs
/*
解题思路:DP
从最后一个字母划分
状态表示:f(i,j) 字符串a的前i个和字符串b的前j个字母组成的公共子序列 的 最大长度
状态转移:如果a[i]==b[j] f(i,j) = f(i-1,j-1) + 1
a[i]!=b[j] f(i,j) = max(f(i-1,j) , f(i,j-1))
*/
#include <iostream>
using namespace std;
const int N = 1010;
int n, m;
char a[N], b[N];
int f[N][N];
int main() {
cin >> n >> m >> a + 1 >> b + 1;//字符串从下标1开始存
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i] == b[j]) {
f[i][j] = f[i - 1][j - 1] + 1;
} else {
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
}
}
cout << f[n][m] << endl;
return 0;
}
//01背包问题
/*
状态表示:
f[i][j] 表示 可选物品前i件 背包容量为j时的 最大背包价值
第i件物品重量为w[i],价值为c[i]
状态转移方程:
f[i][j] = max(f[i-1][j] , f[i-1][j-w[i]] + c[i]); // 比较两种方案
*/
#include <iostream>
using namespace std;
int N,V,v[1001],w[1001],f[1001][1001];
int main(){
cin>>N>>V;
for(int i=1;i<=N;i++){ //n件物品的重量和价值
cin>>v[i]>>w[i];
}
for(int i=1;i<=N;i++){//可选物品数 选择在增加
for(int j=1;j<=V;j++){ //对应你的付出代价的数量
if(j>=v[i]){ //能选新物品
f[i][j]=max(f[i-1][j] , f[i-1][j-v[i]] + w[i]); // 比较两种方案
}else{
f[i][j]=f[i-1][j]; //选不了新物品 直接采用老方案
}
}
}
cout<<f[N][V];
return 0;
}
//一维背包 (优化空间)
#include <bits/stdc++.h>
using namespace std;
int f[1001]={0},N,V,w[1001],v[1001];
int main(){
cin >> N >> V;
for(int i=1;i<=N;i++){
cin >> v[i] >> w[i] ;
}
for(int i=1;i<=N;i++){
for(int j=V;j>=v[i];j--){ //从后向前推 防止重复选择
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
cout << f[V];
return 0;
}
// 完全背包问题
#include<bits/stdc++.h>
using namespace std;
int n, m, v[31], w[31], f[201];
int main(){
cin >> m >> n;
for(int i = 1; i <= n; i++) cin >> w[i] >> v[i];
for(int i = 1; i <= n; i++){
for(int j = w[i]; j <= m; j++){ // 从能选新用品的位置开始推。增加一个体积,都考虑再加一件新物品。
if(f[j-w[i]] + v[i] > f[j])
f[j] = f[j-w[i]] + v[i];
}
}
cout << "max=" << f[m];
return 0;
}
//多重背包(1)
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int n, m, w[N], v[N], s[N], f[N];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){ // 输入物品的代价 价值 件数
cin >> w[i] >> v[i] >> s[i];
}
for(int i = 1; i<= n; i++){
for(int j = 1; j <= s[i]; j++){ // 一件一件的加物品
for(int k = m; k >= w[i]; k--){ //01 背包 必须从后往前推 避免重复取新物品
f[k] = max(f[k], f[k - w[i]] + v[i]);//选或者不选新物品两种方案
}
}
}
cout << f[m];
return 0;
}
// 多重背包(2) 二进制优化
#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
int n, m, w[N], v[N], s[N], f[N], num;
struct good{
int w, v;
}goods[N * 20];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){ // 输入同时进行二进制优化 s[i]件 优化成 log(s[i]) 件
cin >> w[i] >> v[i] >> s[i];
int k = 1; // 每次分割 k件
while(k <= s[i]){ // 可以分割出k件套
goods[++num] = {k*w[i], k*v[i]};
s[i] -= k;
k *= 2;
}
if(s[i] > 0) goods[++num] = {s[i]*w[i], s[i]*v[i]};// 剩下的部分作为整体
}
// 01背包模板
for(int i = 1; i <= num; i++){
for(int j = m; j >= goods[i].w; j--){
f[j] = max(f[j], f[j - goods[i].w] + goods[i].v);
}
}
cout << f[m];
return 0;
}
// 快速幂
/*
算法思路:将b转换成几个1 2 4 8 ....等二进制数字和 利用位运算
*/
#define ull unsigned long long
ull quick_pow(ull a,ull b,ull p)
{
ull result=1%p;
a=a%p;
while(b!=0)
{
if(b&1) result=result*a%p;
a=a*a%p;
b>>=1;
}
return result;
}