【机试C++】题目合集-2
往期
题单
并查集
- P3367 【模板】并查集【路径压缩】【模板题】
- P1551 亲戚 【路径压缩】【模板题】非按秩合并
- P1536 村村通【连通块】可以使用按秩合并
贪心
- P1803 凌乱的yyy / 线段覆盖【排序(结束时间)】
- 管道 3544【贪心,区间合并】
- 跳石头 364【二分,贪心,最小值最大化问题】
队列
- P1996 约瑟夫问题【循环队列】
- CLZ银行问题 1113【模拟】
- 电子产品加工 8486【模拟】
- 餐厅排队 3745【双端队列】
- P1090 [NOIP 2004 提高组] 合并果子【优先队列】【小顶堆】
- Windows的消息队列【优先队列】【自定义排序】
- 小蓝的智慧拼图购物【优先队列】【贪心】【大顶堆】
栈
二叉树 (大多和搜索(BFS、DFS)、路径计算、动态规划有关,下面是二叉树概念题)
- 完全二叉树的权值 183 【经典】构造完全二叉树,实际上一个数组就够了,不需要Node节点
- B3642 二叉树的遍历 【递归构造二叉树】【前、中、后遍历】
- American Heritage 【经典】前序、中序 → 后序
- 求先序排列 【经典】中序、后序 → 前序
DFS
- P1706 全排列问题【全排列】
- P1596 [USACO10OCT] Lake Counting S 【连通块个数】
- P1219 [USACO1.5] 八皇后 Checker Challenge【剪枝】【经典】
BFS
- 844. 走迷宫 【模板题】
- P1443 马的遍历 【最短路】
- P1141 01迷宫 【连通块】【BFS】【DFS】
- P1162 填涂颜色【连通块】【BFS】【DFS】在数组外面加一圈开始染色
DP
- 198. 打家劫舍【线性DP】
- P1216 [IOI 1994 / USACO1.5] 数字三角形 Number Triangles【线性DP】
- B3637 最长上升子序列【线性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表示行数、列数。

点击查看代码
#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】

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;
}

浙公网安备 33010602011771号