算法
1 排序
1.1 直接插入排序
代码:
void insertSort(int arr[], int n) {
int temp, j;
for (int i = 1; i < n; i++) {
temp = arr[i];
j = i - 1;
while (j >= 0 && temp < arr[j]) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = temp;
}
}
1.2 简单选择排序
代码:
void selectSort(int arr[], int n) {
int k, temp; // 最小值在数组中下标
for (int i = 0; i < n; i++) {
k = i;
for (int j = i + 1; j < n; j++)
if (arr[k] > arr[j])
k = j;
temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
}
1.3 冒泡排序
步骤:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值。
- 重复以上的步骤,每次比较次数-1,直到不需要比较
代码:
void bubleSort(int arr[], int n) {
int flag, temp;
for (int i = n - 1; i >= 1; i--) {
flag = 0;
for (int j = 1; j <= i; i++) {
if (arr[j - 1] > arr[j]) {
temp = arr[j];
arr[j] = arr[j - 1];
arr[j - 1] = temp;
flag = 1;
}
}
if (flag == 0) return;
}
}
1.4 希尔排序
void shellSort(int arr[], int n) {
int temp;
for (int gap = n / 2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
temp = arr[i];
int j;
for (j = i; j >= gap && arr[j - gap] > temp; j -= gap)
arr[j] = arr[j - gap];
arr[j] = temp;
}
}
}
1.5 快速排序
void quickSort(int arr[], int low, int high) {
int temp;
int i = low, j = high;
if (low < high) {
temp = arr[low];
while (i < j) {
while (j > i && arr[i] >= temp) j--;
if (i < j) {
arr[i] = arr[j];
i++;
}
while (i < j && arr[i] < temp) i++;
if (i < j) {
arr[i] = arr[j];
j--;
}
}
arr[i] = temp;
quickSort(arr, low, i - 1);
quickSort(arr, i + 1, high);
}
}
2 搜索
2.1 深度优先搜索(DFS)
2.1.1 回溯算法模板
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
2.1.2 走迷宫(DFS)
题目描述:一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由n*n的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不能通行。同时当Extense处在某个格点时,他只能移动到东南西北(或者说上下左右)四个方向之一的相邻格点上,Extense想要从点A走到点B,问在不走出迷宫的情况下能不能到。如果起点或者终点有一个不能通行(为#),则看成无法办到。
输入格式:第1行是一个正整数n(1<=n<=100),表示迷宫的规模是n*n的。接下来是一个n*n的矩阵,矩阵中的元素为 . 或者 # 。再接下来一行是4个整数ha,la,hb,lb,描述A处在第ha行,第la列,B处在第hb行,第lb列。注意到ha,la,hb,lb全部是从0开始计数的。
输出格式:能办到则输出“YES”,否则输出“NO”。
样例输入:
3
. # #
. # #
. . .
0 0 2 2
样例输出:
YES
代码:
#include <bits/stdc++.h>
using namespace std;
char mp[100][100]; // 存储迷宫地区
bool vis[100][100] = {false}; // 记录是否走过,初始为false,走过记为true
int dir[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; // 方向数组
int n, sx, sy, ex, ey; // 地图尺寸,起点、终点坐标
bool f = false;
// 判断坐标在不在地图范围内
bool in(int x, int y) {
return x >= 0 && x < n && y >= 0 && y < n;
}
// 走迷宫dfs递归函数
void dfs(int x, int y) {
// 若到达终点,退出
if (x == ex && y == ey) {
f = true;
return;
}
for (int i = 0; i < 4; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
// 条件为:在地图内,路未走过,路可以走
// 必须先判断在不在地图内,不然vis[tx][ty]可能会发生数组越界
if (in(tx, ty) && mp[tx][ty] != '#' && !vis[tx][ty]) {
vis[x][y] = true;
dfs(tx, ty);
vis[x][y] = false;
}
}
}
int main()
{
// 输入地图尺寸
cin >> n;
// 输入迷宫地图
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> mp[i][j];
}
}
// 输入起点、终点坐标
cin >> sx >> sy >> ex >> ey;
// 若起点或终点为 # ,则输出no
if (mp[sx][sy] == '#' || mp[ex][ey] == '#') {
cout << "NO" << endl;
} else {
// 搜索,走迷宫
dfs(sx, sy);
// 判断是否到达
if (f == true) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
return 0;
}
2.1.3 中国象棋(马)
题目描述:中国象棋博大精深,其中马的规则最为复杂,也是最难操控的一颗棋子。我们都知道象棋中马走“日“,比如在(2,4)位置的一个马,跳一步能到达的位置有(0,3),(0,5),(1,2),(1,6),(3,2),(3,6),(4,3),(4,5)。
蒜头君正在和花椰妹下棋,蒜头君正在进行战略布局,他需要把在(x,y)位置的马跳到(x',y')位置以达到威慑的目的。
但是棋盘大小有限制,棋盘是一个10×9的网格,左上角坐标为(0,0),右下角坐标为(9,8),马不能走出棋盘,并且有些地方已经有了棋子,马也不能跳到有棋子的点。
蒜头君想知道,在不移动其他棋子的情况下,能否完成他的战略目标。
输入格式:
输入一共10行,每行一个长度为9的字符串。输入表示这个棋盘,我们用 '·' 表示空位置,用'#'表示该位置有棋子,用·S'表示初始的马的位置,用'T'表示马需要跳到的位置。输入保证一定只存在一个'S'和一个'T'。
输出格式:
如果在不移动其他棋子的情况下,马能从'S'跳到'T',那么输出一行"Yes”,否则输出一行"No”。
样例输入:
.#....#S#
..#.#.#..
..##.#..#
......##.
...T.....
...#.#...
...#.....
...###...
.........
.##......
样例输出:
Yes
代码:
#include <bits/stdc++.h>
using namespace std;
string s[10]; // 存储棋盘
bool f = false; // 是否抵达终点
bool vis[10][9]; // 是否走过
int dir[8][2] = {{2, 1}, {1, 2}, {-1, 2}, {-2, 1}, {-2, -1}, {-1, -2}, {1, -2}, {2, -1}};
// 判断在棋盘内
bool in(int x, int y) {
return x >=0 && x < 10 && y >= 0 && y < 9;
}
void backtrack(int x, int y) {
vis[x][y] = true;
if (f == true) {
return;
}
if (s[x][y] == 'T') {
f = true;
return;
}
for (int i = 0; i < 8; i++) {
int tx = x + dir[i][0];
int ty = y + dir[i][1];
if (in(tx, ty) && s[tx][ty] != '#' && !vis[tx][ty]) {
backtrack(tx, ty);
}
}
}
int main() {
int sx, sy; // 起点坐标
// 输入棋盘
for (int i = 0; i < 10; i++) {
cin >> s[i];
}
// 初始化vis
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 9; j++) {
vis[i][j] = false;
}
}
// 找起点坐标
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 9; j++) {
if (s[i][j] == 'S') {
sx = i;
sy = j;
}
}
}
backtrack(ex, ey);
// 输出答案
if (f == true) {
cout << "Yes" << endl;
} else {
cout << "No" << endl;
}
return 0;
}
2.1.4 从n个数中选k个数之和为sum
题目描述:
从n个数中选k个数之和为sum,共有ans种
样例输入:
5 3 9
1 2 3 4 5
样例输出:
2
代码:
#include <bits/stdc++.h>
using namespace std;
// 从n个数中选k个数之和为sum,共有ans种
int n, k, sum, ans = 0;
int a[40];
// i:第几个数,cnt:选了几个数,s:总和
void dfs(int i, int cnt, int s) {
if (i == n) {
if (cnt == k && s == sum) {
ans++;
}
return;
}
// 不选
dfs(i + 1, cnt, s);
// 选
dfs(i + 1, cnt + 1, s + a[i]); // 这里必须写cnt+1或者++cnt,不能写cnt++
}
int main() {
cin >> n >> k >> sum;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
dfs(0, 0, 0);
cout << ans << endl;
return 0;
}
2.2 广度优先搜索(BFS)
- 一般是求最短路径问题
2.2.1 广度优先搜索模板
void bfs(起始点) {
将起始点放入队列中;
标记起点访问;
while(如果队列不为空) {
访问队列中队首元素x;
删除队首元素;
for (x 所有相邻点) {
if (该点未被访问过且合法) {
将该点加入队列末尾;
}
}
}
队列为空,广搜结束;
}
2.2.2 走迷宫(BFS)
#include <bits/stdc++.h>
using namespace std;
char mp[100][100]; // 存储迷宫地区
bool vis[100][100] = {false}; // 记录是否走过,初始为false,走过记为true
int dir[4][2] = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; // 方向数组
int n, sx, sy, ex, ey; // 地图尺寸,起点、终点坐标
bool f = false;
// 迷宫结点结构体
struct Node {
int x, y, step;
Node(int x, int y, int step) {
this->x = x;
this->y = y;
this->step = step;
}
};
// 判断坐标在不在地图范围内
bool in(int x, int y) {
return x >= 0 && x < n && y >= 0 && y < n;
}
// 走迷宫bfs函数
void bfs() {
queue<Node> q;
// 起点加入队列
q.push(Node(sx, sy, 0));
// 标记起点访问
vis[sx][sy] = true;
while (!q.empty()) {
// 访问队列中队首元素now
Node now = q.front();
// 删除队首元素
q.pop();
for (int i = 0; i < 4; i++) {
int tx = now.x + dir[i][0];
int ty = now.y + dir[i][1];
// 该点未被访问过且合法
if (in(tx, ty) && mp[tx][ty] != '#' && !vis[tx][ty]) {
// 若到终点
if (tx == ex && ty == ey) {
f = true;
return;
} else {
// 将该点加入队列末尾
q.push(Node(tx, ty, now.step + 1));
vis[tx][ty] = true;
}
}
}
}
}
int main()
{
// 输入地图尺寸
cin >> n;
// 输入迷宫地图
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> mp[i][j];
}
}
// 输入起点、终点坐标
cin >> sx >> sy >> ex >> ey;
// 若起点或终点为 # ,则输出no
if (mp[sx][sy] == '#' || mp[ex][ey] == '#') {
cout << "NO" << endl;
} else {
bfs();
// 判断是否到达
if (f == true) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
return 0;
}
3 动态规划
3.1 动态规划解题步骤
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
3.2 01背包
题目描述:
有n件物品和一个最多能背重量为 w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。
输入格式:
第一行输入n件商品,和背包容量b。
第二行开始,输入n行数据,第n行代表商品编号为n-2的商品信息,一行的第一个数据是商品重量w,第二个数据是商品价值v。
输出格式:
打印背包里最大的物品价值总和。
样例输入:
3 4
1 15
3 20
4 30
样例输出:
35
思路:
参考:代码随想录 - 01背包
-
确定dp数组(dp table)以及下标的含义:i:商品编号,j:背包容量,dp[i][j]:从编号 0 - i 的商品中任意取,再放进容量为 j 的背包中的最大价值。
-
确定递推公式:
-
不放商品 i :dp[i][j] = dp[i - 1][j],第 i 个商品若不放,则0 - i 中任取与 0 - i - 1一样。
-
放商品 i :dp[i][j] = dp[i - 1][j - weight[i]] + value[i],现确定放商品 i ,所以要加上value[i],然后还要加上上一状态,上一状态要除去商品 i ,要在 0 - i - 1 中任取,背包容量也要减去商品 i 的重量,即dp[i - 1][j - weight[i]] + value[i]。
所以递推公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
-
-
dp数组如何初始化:
- 初始化由递推公式确定,因为递推公式中有 i - 1 ,所以第一行需要初始化。然后,递推公式中有 j - weight[i] ,j 必须 >= weight[i] 时,才能放进背包,即 j - weight[i] >= 0 ,所以 j < weight[0] 的列需要初始化。
- 如果背包容量 j < weight[0] 的话,无论是选取哪些物品,背包价值总和一定为0,所以 j < weight[0] 的列初始化为0 。
- 第一行,dp[0][j],即:i 为 0 ,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
-
确定遍历顺序:先遍历商品还是背包都可以。
-
举例推导dp数组
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAX = 100;
int n, b; // n:商品种类数量,b:背包容量
int weight[MAX] = {0}, value[MAX] = {0}; // weight:商品重量,value:商品价值
vector<vector<int> > dp(MAX, vector<int>(MAX, 0)); //dp数组,表示最大价值
void test_01bag() {
// 初始化
for (int j = weight[0]; j <= b; j++) {
dp[0][j] = value[0];
}
// 遍历
for (int i = 1; i < n; i++) { // 遍历商品
for (int j = weight[0]; j <= b; j++) { // 遍历背包容量
if (j < weight[i]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
}
}
}
}
int main() {
// 数据输入
cin >> n >> b;
for (int i = 0; i < n; i++) {
cin >> weight[i] >> value[i];
}
// 填dp数组
test_01bag();
// 打印答案
cout << dp[n - 1][b] << endl;
return 0;
}
4 数论
4.1 最大公约数(gcd)和最小公倍数(lcm)
先使用辗转相除法求出gcd,再利用性质:\(lcm(a, b) = \frac{ab}{gcd(a, b)}\),求出lcm
代码:
#include <iostream>
#include<algorithm>
using namespace std;
// 欧几里得算法,辗转相除法
int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
int main() {
int n, m;
cin >> n >> m;
int GCD1 = gcd(n, m);
cout << GCD1 << endl; // 最大公约数
cout << n / GCD1 * m << endl; // 最小公倍数,这里先除再乘,避免n×m可能过大
// 使用c++的algorithm库函数__gcd(),返回值为最大公约数
int GCD2 = __gcd(n, m);
cout << GCD2 << endl; // 最大公约数
cout << n / GCD2 * m << endl; // 最小公倍数
return 0;
}
4.2 质数筛选
- 判断质数函数
// 判断质数
bool is_prime(int n)
{
if (n <= 1) {
return false;
}
if (n == 2) {
return true;
}
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
- 质数筛选(打印1~100质数)
#include <iostream>
using namespace std;
int main() {
bool is_prime[105];
// 先全部初始化为true
for (int i = 2; i <= 100; i++) {
is_prime[i] = true;
}
// 遍历2~10,其倍数记为false
for (int i = 2; i * i <= 100; i++) {
if (is_prime[i] == true) {
for (int j = i * i; j <= 100; j += i) {
is_prime[j] = false;
}
}
}
// 打印结果
for (int i = 2; i <= 100; i++) {
if (is_prime[i] == true) {
cout << i << " ";
}
}
return 0;
}
4.3 全排列
#include <bits/stdc++.h>
using namespace std;
int a[3] = {1, 2, 3};
int n = 3;
void fullPermutation(int array[], int left, int right) {
if (left == right) {
for (int i = 0; i < n; i++) {
cout << array[i] << " ";
}
cout << endl;
return;
}
for (int i = left; i <= right; i++) {
swap(array[i], array[left]);
fullPermutation(array, left + 1, right);
swap(array[i], array[left]);
}
}
int main() {
fullPermutation(a, 0, n - 1);
return 0;
}
4.4 快速幂
题目描述:
求 \(a^b\%c\) 的值。其中 \(a,b,c\) 是整数,且 \(0 < a,c < 10^9, 0 < b < 10^{18}\) 。
代码:
#include <bits/stdc++.h>
using namespace std;
long long fast_power(long long a, long long b, long long c) {
long long ans = 1;
a %= c;
while (b) {
if (b & 1) { // b % 2 == 1, 奇数
ans = (ans * a) % c;
}
a = (a * a) % c;
b >>= 1; // b /= 2
}
return ans;
}
int main() {
cout << fast_power(2, 5, 7) << endl; // 2^5 % 7 = 4
return 0;
}
4.5 质因数分解
题目链接: 蓝桥杯2022年第十三届省赛真题-质因数个数
题目描述:
给定正整数 n,请问有多少个质数是 n 的约数。
输入格式:
输入的第一行包含一个整数 n。
输出格式:
输出一个整数,表示 n 的质数约数个数。
样例输入:
396
样例输出:
3
提示:
396 有 2, 3, 11 三个质数约数。
对于 30% 的评测用例,1 ≤ n ≤ 10000。
对于 60% 的评测用例,1 ≤ n ≤ 109。
对于所有评测用例,1 ≤ n ≤ 1016。
思路:
对于任意一个正整数 ,可以将它分解成n个质因子的乘积
例如:
36=2*2*2*3*3
20=2*2*5
由此定理可以发现,对于正整数来说,它的任意一个因数都是它质因数的乘积,所有因数即是它质因数乘积的各种组合,由此可以快速的分解出一个数的所有因数
代码:
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
LL n;
LL cnt = 0;
int main() {
cin >> n;
if (n == 1) {
cout << 0 << endl;
return 0;
}
LL i;
for (i = 2; i <= sqrt(n) && n > 1; i++) {
if (n % i == 0) cnt++;
while (n % i == 0) { // 去掉那些相同的质因子i
n /= i;
}
}
if (i > 1) cnt++; // 如果最后剩下的大于1,就加上余下的那个质因子
cout << cnt << endl;
return 0;
}
标签:arr,temp,int,++,算法,&&,dp
From: https://www.cnblogs.com/xiufanivan/p/17410027.html