😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!💪💪💪
文章目录
- 前言
- 算法实验复习
- 一、基本算法题目
- 1.二分查找
- 2.快速排序
- 3.归并排序
- 4.最长公共子序列
- 5.最大子数组和
- 6.最长上升子序列
- 7.八皇后
- 8.加1乘2平方
- 9.电子老鼠闯迷宫
- 10.素数环
- 11.斐波那契数列
- 12.01背包
- 二、进阶算法题目
- 1.八数码
- 2.活动安排
- 3.循环赛日程表
- 4.计算矩阵连乘
- 5.图的m着色问题
- 6.装载问题
- 7.图的最短路径
- 后记
前言
本篇文章送给每一个正在复习算法实验的同学❤️,愿大家都能满分过实验。本篇文章包含了常见算法题目,方便大家查漏补缺。算法实验不能光背代码,理解其中的意义,并且能实现出来才算真的掌握。能找到实验OJ的题目我会给出链接,一定要自己敲上一回,没有在线OJ的也要自己在本地IDE上实现一下。
这里要提醒一句,如果你是西工大考生,西工大算法实验考试是在做实验的网站上的,编译器非常弱,大概能支持到C++03左右的水准,vector<vector<int>>
以及pair
等是使用不了的,所以,要尽量避免用C++的一些语法。
- 算法实现中的pair可以用两个int代替,一个存x坐标,一个存y坐标
- vector<vector<int>> 这个是个二维数组,不会C++的同学可以直接提前开一个二维数组
算法实验复习
一、基本算法题目
这部分是必须要掌握的算法题目,可以说只要掌握了这部分,就不会挂科了
1.二分查找
🍬原题链接
:二分查找
🍭算法思想
:
🍡代码实现
:
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;
while (left <= right)
{
int mid = (left + right) / 2;
if (nums[mid] == target)
return mid;
else if (nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return -1;
}
};
2.快速排序
🍬原题链接
:排序数组
🍭算法思想
:
任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右
子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
🍡代码实现
:
#include <iostream>
using namespace std;
const int N = 100010;
int q[N];
void quick_sort(int* q, int left, int right)
{
if (left >= right)
return;
int i = left - 1, j = right + 1, key = q[left + right >> 1];
while (i < j)
{
do i++; while (q[i] < key);// i之前的数全部小于等于key
do j--; while (q[j] > key);// i之后的数全部大于等于key
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, left, j);
quick_sort(q, j + 1, right);
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &q[i]);
quick_sort(q, 0, n - 1);
for (int i = 0; i < n; i++) printf("%d ", q[i]);
return 0;
}
3.归并排序
🍬原题链接
:排序数组
🍭算法思想
:
基本分治思想,这里不多赘述。
- 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
- 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
- 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
- 重复步骤 3 直到某一指针达到序列尾;
- 将另一序列剩下的所有元素直接复制到合并序列尾。
🍡代码实现
:
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int v[N], tmp[N];
void merge_sort(int v[], int left, int right)
{
if (left >= right)
return;
int mid = left + right >> 1;
merge_sort(v, left, mid);
merge_sort(v, mid + 1, right);
int i = left, j = mid + 1;
int k = 0;
while (i <= mid && j <= right)
{
if (v[i] < v[j]) tmp[k++] = v[i++];
else tmp[k++] = v[j++];
}
while (i <= mid)
tmp[k++] = v[i++];
while (j <= right)
tmp[k++] = v[j++];
// 临时数组每次是从0开始使用的,所以j从0开始
for (i = left, j = 0; i <= right; ++i, ++j)
v[i] = tmp[j];
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; ++i)
scanf("%d", &v[i]);
merge_sort(v, 0, n - 1);
for (int i = 0; i < n; ++i)
printf("%d ", v[i]);
return 0;
}
4.最长公共子序列
🍬原题链接
:最长公共子序列
🍭算法思想
:
-
这道题是一道非常经典的动态规划题目,实现的思路也比较简单。
-
状态:
f(i,j)
—— s1前i个字符和s2前j个字符最长公共序列。 -
初始值: f ( i , 0 ) = f ( 0 , j ) = 0 f(i,0)=f(0,j)=0 f(i,0)=f(0,j)=0
-
状态转移方程:如果 s 1 [ i − 1 ] = = s 2 [ i − 1 ] s1[i-1] == s2[i-1] s1[i−1]==s2[i−1],
f ( i , j ) = m a x ( f ( i − 1 , j − 1 ) + 1 , f ( i − 1 , j ) , f ( i , j − 1 ) ) f(i,j)=max(f(i-1,j-1) + 1, f(i-1,j),f(i,j-1)) f(i,j)=max(f(i−1,j−1)+1,f(i−1,j),f(i,j−1))
反之,
f ( i , j ) = m a x ( f ( i − 1 , j − 1 ) , f ( i − 1 , j ) , f ( i , j − 1 ) ) f(i,j)=max(f(i-1,j-1) , f(i-1,j),f(i,j-1)) f(i,j)=max(f(i−1,j−1),f(i−1,j),f(i,j−1)) -
结果: f ( m , n ) f(m,n) f(m,n)
🍡代码实现
:
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
vector<vector<int>> dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));
for (int i = 1; i <= text1.size(); ++i)
{
for (int j = 1; j <= text2.size(); ++j)
{
if (text1[i - 1] == text2[j - 1]) // 两字符相等
{
dp[i][j] = max(dp[i - 1][j - 1] + 1, max(dp[i][j - 1], dp[i - 1][j]));
}
else
{
dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
}
}
}
return dp[text1.size()][text2.size()];
}
};
// 纯C实现
int longestCommonSubsequence(char* text1, char* text2) {
int m = strlen(text1), n = strlen(text2);
int dp[m + 1][n + 1];
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= m; i++) {
char c1 = text1[i - 1];
for (int j = 1; j <= n; j++) {
char c2 = text2[j - 1];
if (c1 == c2) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = fmax(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
}
5.最大子数组和
🍬原题链接
:最大子数组和
🍭算法思想
:
-
状态: f ( x ) f(x) f(x) —— 从上一段连续的最大和到 x 位置的最大和
-
状态转移方程: f ( x ) = m a x ( f ( x − 1 ) + a [ x ] , a [ x ] ) f(x)=max(f(x-1) + a[x], a[x]) f(x)=max(f(x−1)+a[x],a[x]) —— 如果上一段的连续最大和与当前数的和大于当前数,就取上一段的连续最大和与当前数的和,反之,取当前数(相当于如果 前面连续串的和的最大值 以及 当前数 相加的和 如果还不如当前数,不如从这一位置重新开始一个连续的子串,反之继续延续前面的连续串)
-
初始值: f ( 0 ) = a [ 0 ] f(0) = a[0] f(0)=a[0] —— 从a[0]开始子串
-
结果:从 f ( 0 ) − f ( n − 1 ) f(0) - f(n-1) f(0)−f(n−1) 中选出最大值。因为连续串不确定,所以最后要判断一下。
-
实例: − 2 , 1 , − 3 , 4 , − 1 , 2 , 1 , − 5 -2 ,1,-3,4,-1,2,1,-5 −2,1,−3,4,−1,2,1,−5
-
序号 0 1 2 3 4 5 6 7 a [ x ] a[x] a[x] -2 1 -3 4 -1 2 1 -5 f ( x ) f(x) f(x) -2 1 -2 4 3 5 6 1
🍡代码实现
:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
// 以第i个数字结尾的最大和
vector<int> dp(nums.size(), 0);
dp[0] = nums[0];
int Max = dp[0];
for (int i = 1; i < nums.size(); ++i)
{
// 前一个dp是否大于0,不大于0说明前面的子序列的和都没用,不如从i开始组成子序列
dp[i] = dp[i - 1] > 0 ? dp[i - 1] + nums[i] : nums[i];
Max = max(dp[i], Max);
}
return Max;
}
};
6.最长上升子序列
🍬原题链接
:最长上升子序列
🍭算法思想
:
-
动态规划经典题目,设
v
为存放输入序列的数组,dp
为动态规划数组。 -
状态:dp[i] —— 前i个元素的最长上升子序列的长度。
-
初始值:dp[i] = 1 ,每一个值都可以自己构成只有一个元素的最长上升子序列。
-
状态转移方程:如果v[i]>vj ,
d p [ i ] = m a x ( d p [ i ] , d p [ j ] + 1 ) dp[i] = max(dp[i],dp[j]+1) dp[i]=max(dp[i],dp[j]+1)
-
返回值:max(dp[i])
🍡代码实现
:
#include <vector>
#include <iostream>
using namespace std;
int main()
{
int n;
while (cin >> n)
{
vector<int> v(n);
for (int i = 0; i < n; ++i)
cin >> v[i];
vector<int> dp(n, 1);// 初始化值都为1
int ans = dp[0];// 记录最大上升序列
for (int i = 1; i < n; ++i)
{
for (int j = 0; j < i; ++j)
{
if (v[j] < v[i])
dp[i] = max(dp[i], dp[j] + 1);
}
ans = max(ans, dp[i]);
}
cout << ans << endl;
}
return 0;
}
7.八皇后
🍬原题链接
:N 皇后
深度优先搜索经典题目,西工大OJ系统这道题我原本写法过不了,因为语法不支持,所以我拿C++写的可以当作参考,我自己代码下面有一个可以过OJ的代码。
🍭算法思想
:
- 根据题意,皇后的同行,同列以及所处的两个斜线不能有皇后,所以这次我们可以选择按行搜索。
- 在每一行选择一个位置放皇后,每层放置皇后时,要根据当前皇后放置情况判断是否能放置,能放置才能进入下一层递归。
- 具体皇后放置判断标准:
- 当有皇后在同一行,同一列,同一斜线上时,返回
false
。 - 斜线判断:同一上斜线的皇后坐标:行数 + 列数 = 定值
- 同一下斜线的皇后坐标:行数 - 列数 = 定值
- 满足在同一条斜线上的直接
false
。
- 当有皇后在同一行,同一列,同一斜线上时,返回
🍡代码实现
:
class Solution {
public:
// allRet代表所有方案 curv为当前皇后放置的位置
// cur为当前查找的行数,当然也能理解为当前皇后的数量 n代表棋盘行数
void DFS(vector<vector<pair<int, int>>>& allRet, vector<pair<int, int>>& curv, int curRow, int n)
{
// 有n位皇后则返回
if(curRow == n)
{
allRet.push_back(curv);
return;
}
// 按列查找
for(int i = 0; i < n; ++i)
{
// 判断放置位置是否合法
if(isVaildPos(curv, curRow, i))
{
curv.emplace_back(curRow, i);
DFS(allRet, curv, curRow + 1, n);
curv.pop_back();
}
}
}
// 判断皇后位置是否合法
bool isVaildPos(vector<pair<int, int>>& curv, int i, int j)
{
for(auto& pos : curv)
{
// 当有皇后在同一行,同一列,同一斜线上时,返回false
// 斜线判断:同一上斜线 -- 行数 + 列数 == 定值
// 同一下斜线 == 行数 - 列数 == 定值
if(pos.first == i || pos.second == j || pos.first + pos.second == i + j
|| pos.first - pos.second == i - j)
return false;
}
return true;
}
// 将全部可能的组合转换为字符串数组
vector<vector<string>> transString(vector<vector<pair<int, int>>>& allRet, int n)
{
vector<vector<string>> vv;
for(auto& vpos : allRet)
{
vector<string> vs(n, string(n, '.'));
for(auto& pos : vpos)
{
vs[pos.first][pos.second] = 'Q';
}
vv.push_back(vs);
}
return vv;
}
vector<vector<string>> solveNQueens(int n) {
vector<vector<pair<int, int>>> allRet;
vector<pair<int, int>> curv;
DFS(allRet, curv, 0, n);
return transString(allRet, n);
}
};
// 纯C实现
#include <iostream>
using namespace std;
int a[8],cnt,usedcol[8],used1[15],used2[15];
void output()
{
cnt++;
cout<<"No "<<cnt<<':'<<endl;
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
if(j==a[i]) cout<<'A';
else cout<<'.';
}
cout<<endl;
}
}
void dfs(int i)
{
if(i==8) {output();return;}
for(int j=0;j<8;j++)
{
if(!usedcol[j]&&!used1[j+i]&&!used2[j-i+7])
{
a[i]=j;
usedcol[j]=used1[j+i]=used2[j-i+7]=1;
dfs(i+1);
usedcol[j]=used1[j+i]=used2[j-i+7]=0;
}
}
return;
}
int main()
{
dfs(0);
return 0;
}
8.加1乘2平方
这道题没找到在线OJ的链接,大家可以在西工大的OJ平台自行查看。
🍭算法思想
:
广度优先搜索,每次入队 x+1,x*2,x^2
,直到找到答案,输出步数即可。
🍡代码实现
:
#include <iostream>
#include <queue>
using namespace std;
struct T
{
int num,cnt;
}e1,e2;
queue <T> q;
bool flag[10001];
int main()
{
int m,n;
cin>>m>>n;
e1.num=m;
e1.cnt=0;
q.push(e1);
flag[e1.num]=true;
while(!q.empty())
{
e2=q.front();
q.pop();
if(e2.num==n)
{cout<<e2.cnt<<endl;break;}
else
{
e1.cnt=e2.cnt+1;
e1.num=e2.num+1;
if(e1.num<=n&&!flag[e1.num])
{
q.push(e1);
flag[e1.num]=true;
}
e1.num=e2.num<<1;
if(e1.num<=n&&!flag[e1.num])
{
q.push(e1);
flag[e1.num]=true;
}
e1.num=e2.num*e2.num;
if(e1.num<=n&&!flag[e1.num])
{
q.push(e1);
flag[e1.num]=true;
}
}
}
}
9.电子老鼠闯迷宫
原题目没有找到,所以我选了一道和原题目思路差不多的题目,把这个掌握,做电子老鼠就不会有问题了。
🍬原题链接
:腐烂的橘子
🍭算法思想
:
-
由于题目要求是返回最小完全腐烂的时间,所以,用深度优先搜索很不好做,并且这道题的腐烂传染方式很适合BFS。
-
首先,将开始腐烂的橘子坐标加入队列,遍历这个橘子后,将其上、下、左、右四个方向的橘子入队(下一轮腐烂的橘子)。
-
接着,按照队列的顺序将橘子出队,遍历并将其上、下、左、右四个方向的橘子结点入队。
-
将一轮腐烂的橘子遍历完后,计时+1分钟。
-
重复2、3过程,直到队列为空。
-
最后,检查是否有新鲜橘子,如果有,返回-1,没有,返回分钟数。
-
🍡代码实现
:
// 算法实现中的queue<pair>可以用两个queue<int>代替,一个存x坐标,一个存y坐标
// vector<vector<int>> 这个是个二维数组,不会C++的同学可以直接提前开一个二维数组
class Solution {
public:
int nextMove[4][2] = {{1, 0}, {-1, 0}, {0 , 1}, {0, -1}};
int orangesRotting(vector<vector<int>>& grid) {
queue<pair<int, int>> q;
// 让坏橘子入队
for(int i = 0; i < grid.size(); ++i)
{
for(int j = 0; j < grid[0].size(); ++j)
{
if(grid[i][j] == 2)
{
q.push(make_pair(i, j));
}
}
}
int cnt = 0;
while(!q.empty())
{
size_t sz = q.size();
// 判断本次有无橘子腐烂
bool flag = false;
while(sz--)
{
auto curPos = q.front();
q.pop();
// 探查四个方向,判断新鲜橘子,将其腐烂并入队
for(int i = 0; i < 4; ++i)
{
int curx = curPos.first + nextMove[i][0];
int cury = curPos.second + nextMove[i][1];
// 判断是否越界
if(curx < 0 || curx >= grid.size() || cury < 0 || cury >= grid[0].size())
continue;
// 检测是否有新鲜橘子
if(grid[curx][cury] == 1)
{
// 本次有橘子腐烂
flag = true;
grid[curx][cury] = 2;
q.push(make_pair(curx, cury));
}
}
}
// 本次有橘子腐烂才能+时间
if(flag)
++cnt;
}
// 判断是否有新鲜橘子
for(int i = 0; i < grid.size(); ++i)
{
for(int j = 0; j < grid[0].size(); ++j)
{
if(grid[i][j] == 1)
{
return -1;
}
}
}
return cnt;
}
};
10.素数环
原题目OJ链接没有找到,所以我用了洛谷一个很相近的替代。遇到原题时,只需要修改输出格式即可。
🍬原题链接
:素数环
🍭算法思想
:
直接暴力回溯即可。
🍡代码实现
:
int n, output[17], cnt = 1;
bool book[17];
void dfs(int x);
void print();
bool isprime(int x);
int main() {
while (cin >> n) {
if (cnt > 1)cout << endl;
cout << "Case " << cnt << ":" << endl;
cnt++;
output[0] = 1;
book[1] = 1;
dfs(1);
memset(book, false, sizeof(book));
}
return 0;
}
void dfs(int x) {
if (x == n && isprime(output[n - 1] + output[0])) {
print();
return;
}
for (int i = 2; i <= n; i++) {
if (!book[i] && isprime(i + output[x - 1])) {
output[x] = i;
book[i] = 1;
dfs(x + 1);
book[i] = 0;
}
}
}
bool isprime(int x) {
if (x < 2)return false;
if (x == 2)return true;
for (int i = 2; i <= sqrt(x); i++) {
if (x % i == 0) return false;
}
return true;
}
void print() {
for (int i = 0; i < n - 1; i++) {
cout << output[i] << " ";
}
cout << output[n - 1] << endl;
}
11.斐波那契数列
基本动态规划,不赘述了。
int dp[n];
int fib(int m)
{
if(m<=2)
return 1;
dp[1]=dp[2]=1;
for(int i=3;i<=m;i++)
dp[i]=dp[i-1]+dp[i-2];
return dp[m];
}
12.01背包
🍬原题链接
:背包问题
🍭算法思想
:
🍡代码实现
:
class Solution {
public:
int backPackII(int m, vector<int>& A, vector<int>& V) {
if (A.empty() || V.empty() || m < 1)
return 0;
const int N = A.size() + 1;
const int M = m + 1;
vector<vector<int> > ret;
ret.resize(N);
// 初始化
for (int i = 0; i != N; ++i) {
ret[i].resize(M, 0);
}
for (int i = 1; i < N; i++)
{
for (int j = 1; j < M; j++)
{
// 如果背包总空间都不够放第i个物品,则放 i 个物品和 放 i - 1 个物品的情况相同
if (j < A[i - 1])
ret[i][j] = ret[i - 1][j];
// 如果空间足够放第i个物品,则要判断是否要放入,详见上文解析
else
ret[i][j] = max(ret[i - 1][j], ret[i - 1][j - A[i - 1]] + V[i - 1]);
}
}
return ret[N - 1][m];
}
};
完全背包思路可以自行了解,这里直接给出代码
class Solution {
public:
int backPackII(int m, vector<int>& A, vector<int>& V) {
if (A.empty() || V.empty() || m < 1)
return 0;
const int N = A.size() + 1;
const int M = m + 1;
vector<vector<int> > ret;
ret.resize(N);
// 初始化
for (int i = 0; i != N; ++i) {
ret[i].resize(M, 0);
}
for (int i = 1; i < N; i++)
{
for (int j = 1; j < M; j++)
{
// 如果背包总空间都不够放第i个物品,则放 i 个物品和 放 i - 1 个物品的情况相同
if (j < A[i - 1])
ret[i][j] = ret[i - 1][j];
// 如果空间足够放第i个物品,则要判断是否要放入,详见上文解析
// 现在不是和上一行比较,而是和本行的进行比较,因为完全背包中物品是没有数量限制的
else
ret[i][j] = max(ret[i - 1][j], ret[i][j - A[i - 1]] + V[i - 1]);
}
}
return ret[N - 1][m];
}
};
二、进阶算法题目
学会上面的基本算法,现在你最起码能拿70分了。下面到了拿100分题目的进阶题目,后面的题目会解析稍微少一些,并且我们的OJ题目都比较老,在线OJ比较难找,所以后面我主要以代码为主,题目参考西工大OJ网站。
1.八数码
洛谷的八数码和我们的有差别,我们的八数码是可以无解的,洛谷的用例都是有解的,所以我选择拿洛谷的题讲思路,下面再贴一个西工大八数码的代码。
🍬原题链接
:八数码难题
🍭算法思想
:
双源BFS,重在代码理解。
🍡代码实现
:
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <queue>
using namespace std;
typedef long long ll;
const ll target = 123456780;
ll start;
int np[4][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
int mat[4][4]; // 数字的矩阵形式
queue<ll> q; // 存储每一步走过的形式
map<ll, int> step; // 存储到每一状态所需要的步数
map<ll, int> status; // 存储这一状态是正着来的还是倒着来的
int BFS()
{
if (target == start)
return 0;
q.push(start);
q.push(target);
step[start] = 0;
step[target] = 1;
status[start] = 1;
status[target] = 2;
int zx, zy; // 存储0在矩阵中的位置
int t = 0;
while (!q.empty())
{
ll now, cur = q.front();
now = cur;
q.pop();
// 将数据放到矩阵中
for (int i = 3; i >= 1; --i)
{
for (int j = 3; j >= 1; --j)
{
mat[i][j] = now % 10;
now /= 10;
if (mat[i][j] == 0)
{
zx = i;
zy = j;
}
}
}
// 四个方向遍历
for (int i = 0; i < 4; ++i)
{
int nx = zx + np[i][0];
int ny = zy + np[i][1];
if (nx < 1 || nx > 3 || ny < 1 || ny > 3)
continue;
// 交换
swap(mat[zx][zy], mat[nx][ny]);
// 得到新的数字
int num = 0;
for (int k = 1; k <= 3; ++k)
for (int j = 1; j <= 3; ++j)
num = num * 10 + mat[k][j];
if (status[num] == status[cur])
{
// 已经遍历过num了
swap(mat[zx][zy], mat[nx][ny]);
continue;
}
if (status[num] + status[cur] == 3)
{
// 两边都遍历过,此时两个步数相加就是最短的步数
return step[num] + step[cur];
}
// 未遍历过才会走到这里
status[num] = status[cur];
step[num] = step[cur] + 1;
q.push(num);
swap(mat[zx][zy], mat[nx][ny]);
}
t++;
if (t >= 34)
return -1;
}
return -1;
}
int main()
{
char c;
for (int i = 0; i < 9; ++i)
{
cin >> c;
if (c == 'x')
start *= 10;
else
{
start *= 10;
start += c - '0';
}
}
cout << BFS() << endl;
return 0;
}
// 西工大版本
#include<iostream>
#include<map>
#include<cstring>
#include<queue>
using namespace std;
string str;
map<string, int> visit;
struct Node
{
string status;
int cnt;
Node(string s = "", int c = 0) :status(s), cnt(c) {}
};
void swap(string& s, int i, int j)
{
char c = s[i];
s[i] = s[j], s[j] = c;
}
string Up(string str) {
int pos = str.find("0");
if (pos <= 2) return "";
swap(str, pos, pos - 3);
return str;
}
string Down(string str) {
int pos = str.find("0");
if (pos >= 6) return "";
swap(str, pos, pos + 3);
return str;
}
string Left(string str) {
int pos = str.find("0");
if (pos == 0 || pos == 3 || pos == 6) return "";
swap(str, pos, pos - 1);
return str;
}
string Right(string str) {
int pos = str.find("0");
if (pos == 2 || pos == 5 || pos == 8) return "";
swap(str, pos, pos + 1);
return str;
}
int bfs()
{
visit[str] = 1;
queue<Node> Q;
Q.push(Node(str));
while (!Q.empty())
{
Node cn = Q.front();
Q.pop();
if (cn.status == "123456780")
return cn.cnt;
string news = Up(cn.status);
if (news != "" && visit[news] != 1)
{
Q.push(Node(news, cn.cnt + 1));
visit[news] = 1;
}
news = Down(cn.status);
if (news != "" && visit[news] != 1)
{
Q.push(Node(news, cn.cnt + 1));
visit[news] = 1;
}
news = Left(cn.status);
if (news != "" && visit[news] != 1)
{
Q.push(Node(news, cn.cnt + 1));
visit[news] = 1;
}
news = Right(cn.status);
if (news != "" && visit[news] != 1)
{
Q.push(Node(news, cn.cnt + 1));
visit[news] = 1;
}
}
return -1;
}
int main()
{
string tmp;
int n = 9;
while (n && cin >> tmp)
{
str += tmp;
n--;
}
cout << bfs() << endl;
return 0;
}
2.活动安排
🍭算法思想
:贪心算法
🍡代码实现
:
#include <iostream>
#include <algorithm>
using namespace std;
struct active
{
int b,e;
}a[1001];
struct rule
{
bool operator()(const active &a,const active &b)
{
return a.e<b.e;
}
};
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i].b>>a[i].e;
sort(a,a+n,rule());
int r=a[0].e;
int ans=1;
for(int i=1;i<n;i++)
{
if(a[i].b>=r)
{
ans++;
r=a[i].e;
}
}
cout<<ans<<endl;
return 0;
}
3.循环赛日程表
🍭算法思想
:
- 按分治策略,我们可以将所有的选手分为两半,则n个选手的比赛日程表可以通过n/2个选手的比赛日程表来决定。
- 递归地用这种一分为二的策略对选手进行划分,直到只剩下两个选手时,比赛日程表的制定就变得很简单。
- 这时只要让这两个选手进行比赛就可以了。
🍡代码实现
:
// 循环赛日程表
#include<iostream>
#include<cmath>
using namespace std;
void schedule(int k, int n, int** array);
int main()
{
int k; // 运动员的人数n=2^k
cout << "运动员的人数为n(n=2^k),请输入k的值:";
cin >> k;
int n = pow(2, k); // 运动员的人数n=2^k
int** array = new int* [n+1]; // 循环赛日程表
for (int i = 0;i < n+1;i++)
array[i] = new int[n+1];
// 填充日程表
schedule(k, n, array);
// 输出日程表
cout << "\n循环赛日程表为:\n";
for (int i = 1;i <= n;i++)
{
for (int j = 1;j <= n;j++)
cout << array[i][j] << " ";
cout << "\n";
}
// 删除二维数组
for (int i = 0;i < n + 1;i++)
delete[] array[i];
delete[] array;
return 0;
}
void schedule(int k, int n, int** array) // 数组下标从1开始
{
for (int i = 1;i <= n;i++) // 第一行排1-n
array[1][i] = i;
int m = 1; // 用来控制每一次填表时i行j列的起始填充位置
for (int s = 1;s <= k;s++) // k指分成k大部分进行填充日程表;s指第几大部分
{
n = n / 2;
for (int t = 1;t <= n;t++) // 第s部分内的循环
{
for (int i = m + 1;i <= 2 * m;i++) // 行
{
for (int j = m + 1;j <= 2 * m;j++) // 列
{
array[i][j + (t - 1) * m * 2] = array[i - m][j + (t - 1) * m * 2 - m]; //左上角等于右下角的值
array[i][j + (t - 1) * m * 2 - m] = array[i - m][j + (t - 1) * m * 2]; //左下角等于右上角的值
}
}
}
m *= 2;
}
}
4.计算矩阵连乘
🍭算法思想
:动态规划
🍡代码实现
:
#include<iostream>
#include <cmath>
using namespace std;
const int N = 11;
int row[N], col[N], n, dp[N][N];
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> row[i] >> col[i];
for(int cnt = 1;cnt<=n;cnt++)
for (int i = 1,j=cnt; j <= n; i++,j++)
{
if (i == j) dp[i][j] = 0;
else
{
dp[i][j] = 100000;
for (int k = i; k < j; k++) //k必须从i开始
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + row[i] * col[k] * col[j]);
}
}
cout << dp[1][n] << endl;
return 0;
}
5.图的m着色问题
给定无向连通图G=(V, E)和m种不同的颜色,用这些颜色为图G的各顶点着色,每个顶点着一种颜色。是否有一种着色法使G中相邻的两个顶点有不同的颜色。这个问题是图的m可着色判定问题。若一个图最少需要m种颜色才能使图中每条边连接的两个顶点着不同颜色,则称这个数m为该图的色数。求一个图的色数m的问题称为图的m可着色优化问题。
🍭算法思想
:
本质上就是图的遍历问题。
🍡代码实现
:
#include<iostream>
#include<vector>
using namespace std;
int n, m, r, ans;
int color[20];
vector <int> b[20]; //邻接表储存无向图
bool check(int v,int k)
{
for (int i = 0; i < b[v].size(); i++)
{
int u = b[v][i];
if (color[u] == k)
return false;
}
return true;
}
void dfs(int v,int c)
{
color[v] = c;
for (int i = 0; i < b[v].size(); i++)
{
int u = b[v][i];
if (color[u]) continue;
for (int k = 1; k <= m; k++)
{
if (check(u, k))
{
dfs(u, k);
int j;
for (j = 0; j < n; j++)
{
if (!color[j])
break;
}
if (j >= n)
ans++;
color[u] = 0; //特别注意此处需要消除标记
}
}
}
}
int main()
{
cin >> n >> r >> m;
for (int i = 0; i < r; i++)
{
int u, v;
cin >> u >> v;
b[u].push_back(v);
b[v].push_back(u);
}
for(int i=1;i<=m;i++)
dfs(0,i);
cout << ans << endl;
return 0;
}
6.装载问题
🍭算法思想
:分治法
🍡代码实现
:
#include<iostream>
using namespace std;
int sign = 0, n, c1, c2, weigh[100];
void FoundPath(int c1_w, int c2_w, int times)
{
if (c1_w > c1 || c2_w > c2) return;
if (times == n)
{
sign = 1;
return;
}
FoundPath(c1_w + weigh[times], c2_w, times + 1);
FoundPath(c1_w, c2_w + weigh[times], times + 1);
return;
}
int main()
{
int cnt = 0, c1_w = 0, c2_w = 0, times = 0, ans[100];
while (cin >> c1 >> c2 >> n)
{
if (n == 0 && c1 == 0 && c2 == 0) break;
for (int i = 0; i < n; i++)
cin >> weigh[i];
sign = 0, c1_w = 0, c2_w = 0, times = 0;
FoundPath(c1_w, c2_w, times);
ans[cnt++] = sign;
}
for (int i = 0; i < cnt; i++)
{
if (ans[i] == 0)
cout << "No" << endl;
else
cout << "Yes" << endl;
}
return 0;
}
7.图的最短路径
🍭算法思想
:迪杰斯特拉算法
🍡代码实现
:
#include<iostream>
using namespace std;
int matrix[100][100]; //邻接矩阵表示带权有向图
bool visited[100]; //标记数组
int dist[100]; //源点到顶点i的最短距离
int path[100]; //记录最短路的路径
int source; //源点
int vertex_num; //顶点数
int arc_num; //边数
void Dijkstra(int source)
{
visited[source] = true;
for (int i = 1; i <= vertex_num; i++)
{
dist[i] = matrix[source][i]; //dist表示当前从源点到顶点i的最短特殊路径
if(dist[i]!=INT_MAX)
path[i] = source; //记录从源点到顶点i的最短特殊路径i的前一个顶点
}
int min_cost; //权值最小
int min_cost_index; //权值最小的下标
for (int i = 1; i <= vertex_num; i++) //找到源点到另外vertex_num-1个点的最短路径
{
min_cost = INT_MAX;
for (int j = 1; j <= vertex_num; j++)
{
if (visited[j] == false && dist[j] < min_cost) //找到权值最小
{
min_cost = dist[j];
min_cost_index = j;
}
}
visited[min_cost_index] = true; //该点已找到,进行标记
for (int j = 1; j <=vertex_num; j++) //更新dist数组
{
if (visited[j] == false &&
matrix[min_cost_index][j] != INT_MAX && //确保两点之间有边
matrix[min_cost_index][j] + min_cost < dist[j])
{
dist[j] = matrix[min_cost_index][j] + min_cost;
path[j] = min_cost_index;
}
}
}
}
void output()
{
for (int i = 1; i <= vertex_num; i++)
{
if (i != source)
{
cout << source << "到" << i << "最短距离是:" << dist[i] << ",路径是:" << i;
for (int t = path[i];t != source; t = path[t])
cout << "--" << t;
cout << "--" << source << endl;
}
}
}
//单源最短路径
int main()
{
cin >> vertex_num >> arc_num;
for (int i = 0; i <= vertex_num; i++)
{
for (int j = 0; j <= vertex_num; j++)
if(i==j)
matrix[i][j]=0;
else
matrix[i][j] = INT_MAX; //初始化matrix数组
}
int u, v, w;
for (int i = 0; i < arc_num; i++)
{
cin >> u >> v >> w;
matrix[u][v] = w; //u到v的长度为w
}
cin >> source;
Dijkstra(source);
output();
return 0;
}
后记
掌握这最常见的19道题一般来说就能拿满分了,多多敲代码,不要只背诵代码。如果你感觉题目有些多,可以提前开始复习,一天做上几道。如果你只想及格,只需要掌握基本算法题目即可,保底70分。
最后,祝愿大家能考的都会,半个小时做完全部。
如果解析有不对之处还请指正,我会尽快修改,多谢大家的包容。
如果大家喜欢这个系列,还请大家多多支持啦😋!
如果这篇文章有帮到你,还请给我一个大拇指 👍和小星星 ⭐️支持一下白晨吧!喜欢白晨这篇文章的话,不如关注👀白晨,以便看到最新更新哟!!!