【机试C++】题目合集-2

往期

题单

并查集

贪心

队列

栈

二叉树 (大多和搜索(BFS、DFS)、路径计算、动态规划有关,下面是二叉树概念题)

DFS

BFS

DP

P1091
P1004
0-1背包问题
P1048
P1616
P1060
P1164
P1049

DFS & BFS

P1596 [USACO10OCT] Lake Counting S 【连通块个数】【DFS】【BFS】

bfs的好处是在求最短步数等答案中第一次搜到的就是答案,但是比较耗空间,在这种矩形的图中跑的比dfs快

点击查看代码(DFS)
#include<iostream>
using namespace std;

int n, m, ans = 0, vis[105][105];
// 一个方格被认为与它的八个邻居相邻
int d[8][2] = {{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
char c[105][105];

void dfs(int x, int y){
    vis[x][y] = 1; // 标记当前节点访问过
    for(int i = 0;i < 8;i++){
        int xx = x + d[i][0];
        int yy = y + d[i][1];
        if(xx >= 1 && xx <= n && yy >= 1 && yy <= m && c[xx][yy] == 'W' && vis[xx][yy] == 0){
            dfs(xx,yy);
        }
    }
}

int main(){
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            cin >> c[i][j];
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
            if(vis[i][j] == 1 || c[i][j] == '.') continue;
            dfs(i, j);
            ans++;
        }
    }
    cout << ans;
    return 0;
}
点击查看代码(BFS)
#include<iostream>
#include<queue>
using namespace std;

typedef pair<int, int> PII;
int n, m, ans = 0, vis[105][105];
// 一个方格被认为与它的八个邻居相邻
int d[8][2] = {{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
char c[105][105];
queue<PII> q;

void bfs(int x, int y){
    q.push({x,y});
    vis[x][y] = 1;
    while(!q.empty()){
        PII tmp = q.front();
        q.pop();
        for(int i = 0;i < 8;i++){
            int xx = tmp.first + d[i][0];
            int yy = tmp.second + d[i][1];
            if(xx >= 1 && xx <= n && yy >= 1 && yy <= m && c[xx][yy] == 'W' && vis[xx][yy] == 0){
                vis[xx][yy] = 1;
                q.push({xx,yy});
            }
        }
    }
}

int main(){
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= m;j++)
            cin >> c[i][j];
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
            if(vis[i][j] == 1 || c[i][j] == '.') continue;
            bfs(i, j);
            ans++;
        }
    }
    cout << ans;
    return 0;
}

P1219 [USACO1.5] 八皇后 Checker Challenge【DFS】【剪枝】

剪枝:发现当前搜索状态不可能产生合法答案时,提前终止这条搜索分支。比如

if(col[i] == 1 || vis1[x - i + n] == 1 || vis2[x + i] == 1) continue;

主、副对角线问题,使用一维数组而不是二维数组。x表示行号,i表示列号,n表示行数、列数。

image

点击查看代码
#include<iostream>
using namespace std;

int n, ans = 0, a[15], vis1[35], vis2[35], col[15];
// 行数,答案个数,n个棋子在每行的位置a[],主对角线vis1[],副对角线vis2[],列位置col[]

void dfs(int x){
    if(x == n + 1){ // n行全部放完,输出
        ans++;
        if(ans <= 3){
            for(int i = 1;i <= n;i++) cout << a[i] << " ";
            cout << endl;
        }
    }
    for(int i = 1;i <= n;i++){ // 遍历第x行的每一列
        if(col[i] == 1 || vis1[x - i + n] == 1 || vis2[x + i] == 1) continue;
        a[x] = i; // 第x行放到第i列上了(后面遍历其他列的时候直接覆盖,不需要重置)
        col[i] = 1; vis1[x - i + n] = 1; vis2[x + i] = 1; // 标记
        dfs(x + 1);
        col[i] = 0; vis1[x - i + n] = 0; vis2[x + i] = 0; // 回溯
    }
}

int main(){
    cin >> n;
    dfs(1);// 从第一行开始遍历
    cout << ans;
    return 0;
}

P1141 01迷宫 【连通块】【bfs】

对于任意一个点,它所能到达的所有点都可以到达它,也就是说路径是可逆的。我们考虑处理出每个点在哪个联通块中,查询时直接输出当前连通块大小。

也可以使用DFS,将BFS逻辑换成递归就可以。但是这道题 n 最大是 1000,也就是最多 100 万个格子。这种情况下递归 DFS 可能因为递归层数过深而栈溢出,所以洛谷提交时 BFS 更稳妥。

点击查看代码(bfs)
#include<iostream>
#include<queue>
using namespace std;

int n, m, cnt, num[1010025]; // cnt第几个连通块,第num[i]个连通块中点的个数
char a[1005][1005]; // 原数据
int vis[1005][1005]; // 访问过
int b[1005][1005]; // 所在连通块是哪一个
int d[4][2] = {{-1,0},{1,0},{0,-1},{0,1}};

void bfs(int x, int y){
    queue<pair<int, int>> q;
    q.push({x,y});
    vis[x][y] = 1;
    b[x][y] = cnt;
    num[cnt]++;
    while(!q.empty()){
        pair<int, int> tmp = q.front();
        q.pop();
        for(int i = 0;i < 4;i++){
            int xx = tmp.first + d[i][0];
            int yy = tmp.second + d[i][1];
            if(xx >= 0 && xx < n && yy >= 0 && yy < n && vis[xx][yy] ==0){
                if(a[xx][yy] == a[tmp.first][tmp.second]) continue;
                q.push({xx, yy});
                vis[xx][yy] = 1;
                b[xx][yy] = cnt;
                num[cnt]++;
            }
        }
    }
}

int main(){
    cin >> n >> m;
    cnt  = 0;
    for(int i = 0;i < n;i++) cin >> a[i];
    for(int i = 0;i < n;i++){
        for(int j = 0;j < n;j++){
            if(vis[i][j] == 0){
                cnt++;
                bfs(i, j);
            }
        }
    }
    while(m-- > 0){
        int x, y;
        cin >> x >> y;
        cout << num[b[x - 1][y - 1]] << endl;
    }
    return 0;
}

P1162 填涂颜色【连通块】【BFS】【DFS】

换成BFS只需要将递归查找的方式换成队列。

点击查看代码(DFS)
#include<iostream>
using namespace std;

int n, a[35][35], vis[35][35];
int d[4][2] = {{0,-1},{0,1},{1,0},{-1,0}};

void dfs(int x, int y){
    vis[x][y] = 1;//可访问到的数据,圈外
    for(int i = 0;i < 4;i++){
        int xx = x + d[i][0];
        int yy = y + d[i][1];
        // 留出矩阵的外面一圈,需要保证所有闭合圈外的数据被访问到
        if(xx < 0 || xx > n + 1 || yy < 0 || yy > n + 1 
               || vis[xx][yy] == 1 || a[xx][yy] == 1) continue;
        dfs(xx, yy);
    }
}

int main(){
    cin >> n;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
            cin >> a[i][j];
    dfs(0, 0); // (0,0)是整个数据矩阵的外围,从这里开始遍历到的都是闭合圈外的
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= n;j++){
            if(vis[i][j] == 0 && a[i][j] == 0) cout << 2 << " ";
            else cout << a[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

动态规划

B3637 最长上升子序列【线性DP】

所谓线性 DP,简单理解就是:按照数组下标从前往后处理,每个状态由前面已经计算出的状态转移得到。

之所以叫状态转移,是因为dp[i]表示一种状态,而这个状态可以从之前的dp[j]状态一步一步转移过来。

点击查看代码
#include<iostream>
using namespace std;

int n, ans = 0;
int a[5005];  // 数据
int dp[5005]; // 以 a[i] 结尾的最长上升子序列长度

int main(){
    cin >> n;
    for(int i = 1;i <= n;i++){
        cin >> a[i];
        dp[i] = 1; // 初始化,每个元素自己都可以构成一个长度为 1 的上升子序列
    }
    // 状态转移:枚举当前结尾位置 i
    for(int i = 1;i <= n;i++){
        for(int j = 1;j < i;j++){ // 枚举 i 前面的每一个位置 j
            // 如果 a[j] < a[i],说明 a[i] 可以接在以 a[j] 结尾的上升子序列后面
            if(a[j] < a[i]) dp[i] = max(dp[i], dp[j] + 1);
        }
        // 在所有 dp[i] 中找最大值(最大子序列长度)
        ans = max(ans, dp[i]);
    }
    cout << ans << endl;
    return 0;
}

198. 打家劫舍【线性DP】

image

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size() == 0) return 0;
        // f(k) = 偷 [0, k-1] 房间中的最大金额
        // f(0) = 0, f(1) = nums[0]
        // f(k) = max{ rob(k-1), nums[k-1] + rob(k-2) }
        vector<int> dp;
        dp.push_back(0);
        dp.push_back(nums[0]);
        for(int i = 2;i <= nums.size();i++){
            dp.push_back(max(dp[i - 1], dp[i - 2] + nums[i - 1]));
        }
        return dp[nums.size()];
    }
};

可以直接定义dp数组大小,并初始化为0。

class Solution {
public:
    int rob(vector<int>& nums) {
        if(nums.size() == 0) return 0;
        // f(k) = 偷 [0, k-1] 房间中的最大金额
        // f(0) = 0, f(1) = nums[0]
        // f(k) = max{ rob(k-1), nums[k-1] + rob(k-2) }
        int n = nums.size();
        vector<int> dp(n + 1, 0);
        dp[0] = 0;
        dp[1] = nums[0];
        for(int i = 2;i <= nums.size();i++){
            dp[i] = max(dp[i - 1], dp[i - 2] + nums[i - 1]);
        }
        return dp[n];
    }
};

P1216 [IOI 1994 / USACO1.5] 数字三角形 Number Triangles【线性DP】

点击查看代码
#include<iostream>
using namespace std;

int n, a[1005][1005], dp[1005][1005];

int main(){
    cin >> n;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= i;j++)
            cin >> a[i][j];
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= i;j++){
            dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + a[i][j];
        }
    }
    int ans = 0;
    for(int i = 1;i <= n;i++){
        ans = max(dp[n][i], ans);
    }
    cout << ans;
    return 0;
}

01 背包

点击查看代码
#include <bits/stdc++.h>
using namespace std;

int c[1005], w[1005]; // 体积和价值
int dp[1005][1005];   // dp[i][j]

int main() {
    int N, V;
    cin >> N >> V;
    for (int i = 1; i <= N; i++) cin >> c[i] >> w[i];

    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= V; j++) {
            // 先继承上一行的结果(对应不选第 i 个物品)
            dp[i][j] = dp[i-1][j];
            // 如果能装下第 i 个物品,再尝试更新
            if (j >= c[i]) {
                dp[i][j] = max(dp[i][j], dp[i-1][j - c[i]] + w[i]);
            }
        }
    }

    cout << dp[N][V] << "\n";
    return 0;
}
posted @ 2026-09-27 12:41  idle_life  阅读(10)  评论(0)    收藏  举报