西北工业大学算法实验机试复习

news2024/12/23 12:42:48

😀大家好,我是白晨,一个不是很能熬夜😫,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!💪💪💪

在这里插入图片描述

文章目录

  • 前言
  • 算法实验复习
    • 一、基本算法题目
      • 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++的同学可以直接提前开一个二维数组

img


算法实验复习


一、基本算法题目


这部分是必须要掌握的算法题目,可以说只要掌握了这部分,就不会挂科了

1.二分查找


image-20221205103524540

🍬原题链接:二分查找

🍭算法思想

img

🍡代码实现

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.快速排序


image-20221205104033964

🍬原题链接:排序数组

🍭算法思想

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右
子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

🍡代码实现

#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.归并排序


image-20221205104033964

🍬原题链接:排序数组

🍭算法思想

基本分治思想,这里不多赘述。

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  4. 重复步骤 3 直到某一指针达到序列尾;
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

🍡代码实现

#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.最长公共子序列


image-20221205105429658

🍬原题链接:最长公共子序列

🍭算法思想

  • 这道题是一道非常经典的动态规划题目,实现的思路也比较简单。

  • 状态: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[i1]==s2[i1]​,
    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(i1,j1)+1,f(i1,j),f(i,j1))
    反之,
    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(i1,j1),f(i1,j),f(i,j1))

  • 结果: 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.最大子数组和


image-20221205110226809

🍬原题链接:最大子数组和

🍭算法思想

  • 状态: 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(x1)+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(n1) 中选出最大值。因为连续串不确定,所以最后要判断一下。

  • 实例: − 2 , 1 , − 3 , 4 , − 1 , 2 , 1 , − 5 -2 ,1,-3,4,-1,2,1,-5 2,1,3,4,1,2,1,5

  • 序号01234567
    a [ x ] a[x] a[x]-21-34-121-5
    f ( x ) f(x) f(x)-21-243561

🍡代码实现

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.八皇后


image-20220919234710173

🍬原题链接: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.电子老鼠闯迷宫


原题目没有找到,所以我选了一道和原题目思路差不多的题目,把这个掌握,做电子老鼠就不会有问题了。

image-20220920091208701

🍬原题链接:腐烂的橘子

🍭算法思想

  • 由于题目要求是返回最小完全腐烂的时间,所以,用深度优先搜索很不好做,并且这道题的腐烂传染方式很适合BFS。

    1. 首先,将开始腐烂的橘子坐标加入队列,遍历这个橘子后,将其上、下、左、右四个方向的橘子入队(下一轮腐烂的橘子)。

    2. 接着,按照队列的顺序将橘子出队,遍历并将其上、下、左、右四个方向的橘子结点入队。

    3. 将一轮腐烂的橘子遍历完后,计时+1分钟。

    4. 重复2、3过程,直到队列为空。

    5. 最后,检查是否有新鲜橘子,如果有,返回-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链接没有找到,所以我用了洛谷一个很相近的替代。遇到原题时,只需要修改输出格式即可。

image-20221205113031875

🍬原题链接:素数环

🍭算法思想

直接暴力回溯即可。

🍡代码实现

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背包


img

🍬原题链接:背包问题

🍭算法思想

img

🍡代码实现

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.八数码


洛谷的八数码和我们的有差别,我们的八数码是可以无解的,洛谷的用例都是有解的,所以我选择拿洛谷的题讲思路,下面再贴一个西工大八数码的代码。

image-20221205114131066

🍬原题链接:八数码难题

🍭算法思想

双源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.循环赛日程表


image-20221205114721073

🍭算法思想

  1. 按分治策略,我们可以将所有的选手分为两半,则n个选手的比赛日程表可以通过n/2个选手的比赛日程表来决定。
  2. 递归地用这种一分为二的策略对选手进行划分,直到只剩下两个选手时,比赛日程表的制定就变得很简单。
  3. 这时只要让这两个选手进行比赛就可以了。

🍡代码实现

// 循环赛日程表
#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分。

最后,祝愿大家能考的都会,半个小时做完全部。img


如果解析有不对之处还请指正,我会尽快修改,多谢大家的包容。

如果大家喜欢这个系列,还请大家多多支持啦😋!

如果这篇文章有帮到你,还请给我一个大拇指 👍和小星星 ⭐️支持一下白晨吧!喜欢白晨这篇文章的话,不如关注👀白晨,以便看到最新更新哟!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/65736.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TC申请是否需要银行转账记录?

【TC申请是否需要银行转账记录&#xff1f;】 答案是毫无疑问的。 根据TE官网公开的文件CCS Certification Procedures V3.0 里面关于TC申请所需的文件指引E2.1.1f&#xff1a;&#xff08;如图&#xff09; 企业在申请与TE相关的认证项目&#xff08;例如GRS/RCS等等&#xf…

iptables 命令和 iptables.service 服务 有什么关系 ?

写在前面 关于iptables 命令 和 iptabls.service 的一些疑惑理解不足小伙伴帮忙指正 傍晚时分&#xff0c;你坐在屋檐下&#xff0c;看着天慢慢地黑下去&#xff0c;心里寂寞而凄凉&#xff0c;感到自己的生命被剥夺了。当时我是个年轻人&#xff0c;但我害怕这样生活下去&…

企业级大数据平台智能运维好帮手——星环科技多模数据平台监控软件Aquila Insight

Aquila Insight介绍 Aquila Insight是星环科技推出的一款多模数据平台监控软件&#xff0c;为企业运维团队提供了一套统一、完整、便捷的智能化运维解决方案。通过丰富的仪表盘管理、告警与通知管理、实时和历史查询语句运行分析、计算和存储引擎的统一监控、完整的日志收集过滤…

编程内功心法「底层原理系列」 回归与本质,让本文带你认识什么是计算机软件系统

前提概要 如果希望可以把计算机编程技术提升到另外一个高度&#xff0c;,那么想要搞清楚什么是软件设计&#xff0c;首先就要理解什么是计算机软件&#xff0c;在这之后&#xff0c;再去考虑为什么对软件进行设计&#xff0c;以及在通常情况下应该怎样设计软件。 什么是计算机…

计算机毕业设计:基于HTML学校后台用户登录界面模板源码

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

KT148A语音芯片SOP外挂功放芯片8002D的说明_V1

目录 一、简介 KT148A语音芯片内置8欧姆0.5W的功放&#xff0c;所以一般的应用场景&#xff0c;播放一些提示音音量是足够的。但是有的需求还需要驱动更大的扬声器&#xff0c;实现更大的播放音量&#xff0c;这里我们给出解决方案。KT148AHAA8002D的组合 二、硬件电路如下-K…

防抖和节流

使用场景&#xff1a; 防抖在连续的事件&#xff0c;只需触发一次回调的场景有&#xff1a; 搜索框搜索输入。只需用户最后一次输入完&#xff0c;再发送请求。 窗口大小resize。只需窗口调整完成后&#xff0c;计算窗口大小。防止重复渲染。 登录、发短信等按钮避免用户点击太…

云原生周刊 | 美国国防部发布零信任战略与路线图

美国国防部发布了到 2027 年截止的零信任战略与路线图&#xff0c;总共有 45 项能力&#xff0c;最终目标是“安全的国防部信息企业”。 开源项目推荐 Extism Extism 是一个 WebAssembly 插件实现框架&#xff0c;它可以给你的应用开发出各种各样的 WebAssembly 插件&#xf…

HTML网页设计——轮滑运动体育类人物介绍主题12页面毕业设计网页

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

[附源码]JAVA毕业设计商店管理系统(系统+LW)

[附源码]JAVA毕业设计商店管理系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&…

电池供电遥测终端RTU 遥测终端机 低功耗遥测采集终端 智能远传 防水IP68

平升电子电池供电遥测终端RTU/遥测终端机/低功耗遥测采集终端是基于4G、5G、NB-IoT网络实现数据采集、远程传输、分析计算、越限报警的智能设备&#xff0c;具有功耗低、IP68防水等特点。特别适合用在无供电条件、防水防尘要求高的监测现场。 随着通信网络更迭、产品持续改进&…

在线文档协同办公-开箱即用demo

在线文档协同办公-开箱即用demo服务安装&#xff08;略&#xff09;下面开始集成打开文件保存文件共同编辑展示一下集成后的效果图服务安装&#xff08;略&#xff09; 这里可以参考前几篇博客内容 Linux版 windows版 下面开始集成 打开文件 1.创建一个空的html文件。 添加…

python匿名函数和高阶函数总结

bilibili 千峰python 学习笔记 匿名函数 lambda函数的语法只包含一个语句&#xff0c;如下&#xff1a; lambda 参数列表: 运算表达式Lambda函数能接收任何数量的参数但只能返回一个表达式的值匿名函数可以执行任意表达式(甚至print函数)&#xff0c;但是一般认为表达式应该有…

UDP-B-L-阿拉伯糖二钠盐,UDP-b-L-arabinopyranose disodium salt,15839-78-8

产品名称&#xff1a;UDP-B-L-阿拉伯糖二钠盐 英文名称&#xff1a;UDP-b-L-arabinopyranose disodium salt CAS号&#xff1a;15839-78-8 分子式&#xff1a;C14H22N2O16P2 分子量&#xff1a;536.27600 产地&#xff1a;西安 规格&#xff1a;1mg 5mg 10mg 纯度&#xff1…

西门子CT重建算法

相对于MR&#xff0c;CT在参数及扫描方面并不是太难&#xff0c;但是CT的图像重建及各种算法则是非常难的&#xff0c;也是比较抽象的。这篇文章介绍CT图像重建算法等相关内容。 CT技术是CT诊断的基础&#xff0c;帮助医务工作者充分掌握CT技术是我们的责任和义务&#xff01; …

Redis原理 - 对象的数据结构(SDS、Inset、Dict、ZipList、QuickList、SkipList、RedisObject)

Redis数据结构 1. SDS Redis 是用 C 语言写的&#xff0c;但是对于 Redis 的字符串&#xff0c;却不是 C 语言中的字符串&#xff08;即以空字符’\0’结尾的字符数组&#xff09;&#xff0c;它是自己构建了一种名为 简单动态字符串&#xff08;simple dynamic string,SDS&am…

如何在ios成功上架android tv?​

如果你是新手&#xff0c;将在这里清晰发布到App Store整个上架流程&#xff0c;还有相应的流程解决方案。​ 如果你上架过iOS APP&#xff0c;这里会了解到有更快捷的上架过程。​ 上架iOS最基本需要一个付费的开发者账号&#xff0c;还没有的话申请一个或者借用。​ 通常也…

7.springboot中整合Jpa多数据源

Springboot 整合spring data jpa多数据源 在使用Mybatis时会涉及多数据源的问题&#xff0c;同样&#xff0c;当我们使用Jpa作为持久化处理方式时&#xff0c;也会涉及多数据源的问题&#xff0c;本节我们来讲解一下Springboot如何整合Spring data Jpa多数据源。 1.创建项目 …

ARM基础(1):Cortex-M3的核心寄存器和特殊寄存器

Cortex-M3处理器的寄存器包括R0~R15和一些特殊的寄存器。其中R0到R12是通用寄存器&#xff0c;但是一些16位的Thumb指令只能访问R0到R7(低寄存器)&#xff0c;而32位的Thumb-2指令则可以访问所有这些寄存器。特殊寄存器只能通过特殊访问指令访问。 文章目录1 核心寄存器1.1 R13…

牛客网刷题【BC33、BC56、BC44、BC91、BC49、写函数求最大值】

目录 一、BC33 计算平均成绩 二、BC56 线段图案 三、BC44 判断整数奇偶型 四、BC91 成绩输入输出问题 五、BC49 判断两个数的大小关系 六、写函数&#xff0c;求最大值 一、BC33 计算平均成绩 #include <stdio.h>int main() {double score0;double sum0;int i…