- 第 199 篇 -
Date: 2026 - 04 - 01 02 | 周三 周四
Author: 郑龙浩(仟墨)
今日算法:DFS & 记忆化搜索 & 回溯
2026-04-01-02-算法打卡day36-DFS专项训练3
文章目录
- 2026-04-01-02-算法打卡day36-DFS专项训练3
13-洛谷P1088-火星人
【题目】
题目描述
人类终于登上了火星的土地并且见到了神秘的火星人。人类和火星人都无法理解对方的语言,但是我们的科学家发明了一种用数字交流的方法。这种交流方法是这样的,首先,火星人把一个非常大的数字告诉人类科学家,科学家破解这个数字的含义后,再把一个很小的数字加到这个大数上面,把结果告诉火星人,作为人类的回答。
火星人用一种非常简单的方式来表示数字――掰手指。火星人只有一只手,但这只手上有成千上万的手指,这些手指排成一列,分别编号为 1 , 2 , 3 , ⋯ 1,2,3,\cdots 1,2,3,⋯。火星人的任意两根手指都能随意交换位置,他们就是通过这方法计数的。
一个火星人用一个人类的手演示了如何用手指计数。如果把五根手指――拇指、食指、中指、无名指和小指分别编号为 1 , 2 , 3 , 4 1,2,3,4 1,2,3,4 和 5 5 5,当它们按正常顺序排列时,形成了 5 5 5 位数 12345 12345 12345,当你交换无名指和小指的位置时,会形成 5 5 5 位数 12354 12354 12354,当你把五个手指的顺序完全颠倒时,会形成 54321 54321 54321,在所有能够形成的 120 120 120 个 5 5 5 位数中, 12345 12345 12345 最小,它表示 1 1 1; 12354 12354 12354 第二小,它表示 2 2 2; 54321 54321 54321 最大,它表示 120 120 120。下表展示了只有 3 3 3 根手指时能够形成的 6 6 6 个 3 3 3 位数和它们代表的数字:
| 三位数 | 代表的数字 |
|---|---|
| 123 123 123 | 1 1 1 |
| 132 132 132 | 2 2 2 |
| 213 213 213 | 3 3 3 |
| 231 231 231 | 4 4 4 |
| 312 312 312 | 5 5 5 |
| 321 321 321 | 6 6 6 |
现在你有幸成为了第一个和火星人交流的地球人。一个火星人会让你看他的手指,科学家会告诉你要加上去的很小的数。你的任务是,把火星人用手指表示的数与科学家告诉你的数相加,并根据相加的结果改变火星人手指的排列顺序。输入数据保证这个结果不会超出火星人手指能表示的范围。
输入格式
共三行。
第一行一个正整数 N N N,表示火星人手指的数目( 1 ≤ N ≤ 10000 1 \le N \le 10000 1≤N≤10000)。
第二行是一个正整数 M M M,表示要加上去的小整数( 1 ≤ M ≤ 100 1 \le M \le 100 1≤M≤100)。
下一行是 1 1 1 到 N N N 这 N N N 个整数的一个排列,用空格隔开,表示火星人手指的排列顺序。
输出格式
N N N 个整数,表示改变后的火星人手指的排列顺序。每两个相邻的数中间用一个空格分开,不能有多余的空格。
输入输出样例 #1
5
3
1 2 3 4 5
1 2 4 5 3
说明/提示
对于 30 % 30\% 30% 的数据, N ≤ 15 N \le 15 N≤15。
对于 60 % 60\% 60% 的数据, N ≤ 50 N \le 50 N≤50。
对于 100 % 100\% 100% 的数据, N ≤ 10000 N \le 10000 N≤10000。
noip2004 普及组第 4 题
【思路】
这道题的题目特别长,整理一下题目大意,大概如下:
至于要求什么,我还是读了很久的题的,因为刚开始没明白M的作用,以及输出的到底是什么
- N表示数组宽度,创建一个nums,存储数组数据
- 题目中的火星人的数字,实际上就是nums数组中第几个排列
- 比如12345,路径1是12345,就是火星人的1,
- 路径2是12354,就是火星人的2,
- 路径3是12435,就是火星人的3
- 路径4是12453,就是火星人的4
- 这里的M,指的就是从1开始,向后数M个
- 例如题目所说,M是3,就是往后数3个,也就是求路径4是什么,即12453
方法1:我是用DFS去对数组进行排列,然后求出第M+1个路径,输出即可,但是这种写法对于我来说太难了,特别的难写
方法2:AI告诉我,还可以用另一个方法,可以用下面的函数
第一次使用这个函数
cpp
#include <algorithm>
bool next_permutation(起始位置, 结束位置);
- 返回 `true`:成功生成了下一个排列
- 返回 `false`:当前已经是最大排列,已将数组变为最小排列
工作原理
假设当前排列是:1 2 3 4 5
调用一次 next_permutation():改变一次数组元素
- 第1次:
1 2 3 5 4 - 第2次:
1 2 4 3 5 - 第3次:
1 2 4 5 3 - ...直到:
5 4 3 2 1
都返回true
当已经是最大排列时(5 4 3 2 1),再调用会:
- 将序列变为最小排列(
1 2 3 4 5) - 返回
false
所以只需要调用M次next_permutation(arr, arr + N)即可,会自动计算该数组进行M次按照字典序排列后的数组是什么
【代码】
cpp
/* 2026-04-01-算法打卡day36-DFS专项训练3
* 13-洛谷P1088-火星人
* Author:郑龙浩
* Date:2026-04-01
* 方法1:手写DFS(比较难写)
* 方法2:使用`next_permutation(arr, arr + N)`函数
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int arr[10000 + 10];
int N, M;
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> N >> M;
for (int i = 0; i < N; i++) cin >> arr[i];
// 核心算法:执行M次"下一个排列"操作
// next_permutation(arr, arr + N) 将数组arr[0..N-1]变为字典序中的下一个排列
// 如果当前已经是最大排列,会变为最小排列(但本题保证结果不会超出范围)
while (M--) {
next_permutation(arr, arr + N);
}
for (int i = 0; i < N; i++) cout << arr[i] << ' ';
return 0;
}
【题目】
【思路】
【代码】
cpp
【题目】
【思路】
【代码】
cpp
14-洛谷B4482-数字游戏II
【题目】
题目描述
千秋正在玩一种数字游戏,这种数字游戏需要在一个 4 × 4 4\times 4 4×4 的网格内填数,每个方格内填入一个 1 ∼ 4 1\sim 4 1∼4 范围内的整数。
游戏胜利当且仅当下面的条件被全部满足:
- 将网格划分为 4 4 4 个 2 × 2 2\times 2 2×2 的子网格,每个子网格中不存在重复的数
- 网格的每行不存在重复的数
- 网格的每列不存在重复的数
子网格的划分如图所示。

千秋已经填好了其中的若干个数,请你完成剩余的数,使得游戏胜利。可能存在多种符合要求的填法,你只需要给出其中一种即可。
输入格式
输入四行,每行四个数,表示网格已经填入的数。
未填入的位置用 0 0 0 表示。
保证已经填入的位置均符合游戏胜利的要求。
输出格式
输出 4 行,每行 4 个整数。表示完成后的游戏局面。
输入输出样例 #1
输入 #1
3 0 4 1
4 1 2 0
1 0 3 2
2 3 1 0
输出 #1
3 2 4 1
4 1 2 3
1 4 3 2
2 3 1 4
说明/提示
用 n n n 表示未填的格子数目。
对于 5 % 5\% 5% 的测试数据, n = 1 n=1 n=1。
对于另外 25 % 25\% 25% 的测试数据, n = 4 n=4 n=4 且恰好有一行未填。
对于另外 30 % 30\% 30% 的测试数据, n = 7 n=7 n=7 且恰好有一行一列未填。
对于 100 % 100\% 100% 的测试数据, 1 ≤ n ≤ 16 1 \le n \le 16 1≤n≤16,已经填好的数不违反游戏胜利的要求,保证存在符合游戏胜利的解。
【思路】
创建grid数组,并且将数据存入
子块:
+-------+-------+
| 子块1 | 子块2 |
| A1 B1 | A2 B2 |
| C1 D1 | C2 D2 |
+-------+-------+
| 子块3 | 子块4 |
| A3 B3 | A4 B4 |
| C3 D3 | C4 D4 |
+-------+-------+
坐标: 列0 列1 列2 列3
行0: (0,0) (0,1) | (0,2) (0,3)
行1: (1,0) (1,1) | (1,2) (1,3)
------------+-------------
行2: (2,0) (2,1) | (2,2) (2,3)
行3: (3,0) (3,1) | (3,2) (3,3)
4 个子块分别是:
- 左上子块:行 0~1,列 0~1
- 右上子块:行 0~1,列 2~3
- 左下行块:行 2~3,列 0~1
- 右下行块:行 2~3,列 2~3
分别给4个子块编号:
- 左上子块 → 编号 (0,0)
- 右上子块 → 编号 (0,1)
- 左下行块 → 编号 (1,0)
- 右下行块 → 编号 (1,1)
先将数据抽象分为4个部分,方便理解的,不是真的分
这是一个 4×4 数独,但有三个限制:
- 每行数字 1~4 不重复
- 每列数字 1~4 不重复
- 每个 2×2 小方块内数字 1~4 不重复
解决思路
因为棋盘只有 16 个格子,用 DFS & 回溯 暴力尝试即可。
步骤:
-
从左到右、从上到下遍历每个格子
-
如果格子已有数字(非0),跳过
-
如果是空白(0),尝试填 1~4:
- 检查填入后是否满足三个条件、限制
- 需要写一个函数,专门判断填入当前数字后,填入的这个数字是否满足条件
- 如果满足,填入数字,继续填下一个格子
- 如果不满足,换下一个数字尝试
- 检查填入后是否满足三个条件、限制
-
如果所有数字都不行,回溯到上一个格子重新填
-
填完最后一个格子时,找到解,结束搜索
【代码】
cpp
/* 2026-04-01-02-算法打卡day36-DFS专项训练3
* 14-洛谷B4482-数字游戏II
* Author:郑龙浩
* Date:2026-04-02
* 算法:
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4;
int grid[N][N] = {0};
vector <pair <int, int>> coordinate; // 存储空白位置的坐标
bool IS (int x, int y, int num) {
// 1 判断行是否存在重复数字
for (int i = 0; i < N; i++) if (num == grid[x][i]) return false;
// 2 判断列是否存在重复数字
for (int i = 0; i < N; i++) if (num == grid[i][y]) return false;
// 3 判断子块内是否存在重复数字
// 确定是属于哪一个子块
int startRow, startCol; // 子块的起始坐标
// 确定x
if (x == 0 || x == 1) startRow = 0;
else startRow = 2;
// 确定y
if (y == 0 || y == 1) startCol = 0;
else startCol = 2;
// 查找子块内是否重复
for (int i = startRow; i < startRow + 2; i++)
for (int j = startCol; j < startCol + 2; j++) {
if (grid[i][j] == num) return false;
}
return true;
}
// pos是存储空白位置坐标的索引值
// pos是还没有添加数值或者正在添加数值的位置
bool dfs(int pos) {
if (pos == coordinate.size()) { // 如果pos到了cnt,就说明0~cnt-1所有的空白位置都已经填入了,此时直接renturn即可,已经填充完毕
return true;
}
int curX = coordinate[pos].first;
int curY = coordinate[pos].second;
for (int num = 1; num <= 4; num++) {
if (IS(curX, curY, num)) {
grid[curX][curY] = num;
if (dfs(pos + 1)) return true;
grid[curX][curY] = 0; // 回溯
}
}
return false;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) {
cin >> grid[i][j];
if (grid[i][j] == 0) {
coordinate.push_back({i, j});
}
}
dfs(0);
for (int i = 0; i < N; i ++) {
// 输出一定要注意:最后一个字符后面不能是' ',所以要让其后面单独输出空格
for (int j = 0; j < N - 1; j++) cout << grid[i][j] << ' ';
cout << grid[i][N - 1] << '\n';
}
return 0;
}
15-洛谷B3862-图的遍历简单版-DFSBFS
【题目】
题目描述
给出 N N N 个点, M M M 条边的有向图,对于每个点 v v v,求 A ( v ) A(v) A(v) 表示从点 v v v 出发,能到达的编号最大的点。
输入格式
第 1 1 1 行 2 2 2 个整数 N , M N,M N,M,表示点数和边数。
接下来 M M M 行,每行 2 2 2 个整数 U i , V i U_i,V_i Ui,Vi,表示边 ( U i , V i ) (U_i,V_i) (Ui,Vi)。点用 1 , 2 , ... , N 1,2,\dots,N 1,2,...,N 编号。
输出格式
一行 N N N 个整数 A ( 1 ) , A ( 2 ) , ... , A ( N ) A(1),A(2),\dots,A(N) A(1),A(2),...,A(N)。
输入输出样例 #1
输入1
4 3
1 2
2 4
4 3
输出
4 4 3 4
说明/提示
- 对于 100 % 100\% 100% 的数据, 1 ≤ N , M ≤ 10 3 1 \leq N,M \leq 10^3 1≤N,M≤103。
【思路】
【代码】
cpp
/* 2026-04-01-02-算法打卡day36-DFS专项训练3
* 15-洛谷B3862-图的遍历简单版-DFSBFS
* Author:郑龙浩
* Date:2026-04-02
* 算法:DFS
* 思路:本来求的是点v能到达的编号最大的点,但是反过来想,从最大的点A(v)往小走,A(v)不就是这些途径的小点的最大点了吗
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int N, M;
// DFS遍历反向图,标记所有能到达当前最大点的节点
// 反向图(v->u表示原图中u能到达v)
// maxPoints 存储每个点能到达的最大编号
// pos 当前遍历的节点
// maxPoint 当前正在遍历的起点编号(即pos的最大可达节点)
void dfs(vector <vector <int>>& graph, bool visited[], int maxPoints[], int pos, int maxPoint) {
visited[pos] = true;
maxPoints[pos] = maxPoint; // 这个点在原图中能到达maxPoint,且maxPoint就是pos最大编号
for (int next = 0; next < graph[pos].size(); next++) {
int nextPoint = graph[pos][next];
if (!visited[nextPoint]) // 如果当前节点没有被访问过,就可以访问
dfs(graph, visited, maxPoints, nextPoint, maxPoint);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> N >> M;
vector <vector <int>> graph (N + 1); // 编号从1开始的,所以多开一个
int u, v;
int M2 = M;
while (M2--) {
cin >> u >> v;
graph[v].push_back(u); // 反向存图,u-> v,变成v->u
}
int maxPoints[N + 1] = {0}; // 存储每个节点的最大点
bool visited[N + 1] = {false}; // 访问过的
// 从编号大的反向寻找
for (int i = N; i >= 1; i--) {
if (visited[i] == false)
dfs(graph, visited, maxPoints, i, i); // 遍历所有比N小的点,并且将他们的大点赋值为i
}
for (int i = 1; i <= N; i++)
cout << maxPoints[i] << ' ';
return 0;
}
【题目】洛谷P4961-小埋与扫雷
题目背景
小埋总是在家中打游戏,一天,她突然想玩Windows自带的扫雷,在一旁的哥哥看见了,想起了自己小时候信息课在机房玩扫雷的日子,便兴致勃勃地开始教小埋扫雷。然而,小埋还是不明白 3 b v \mathrm{3bv} 3bv(Bechtel's Board Benchmark Value,每局将所有非雷的方块点开所需最少左键点击数,参见扫雷网的教程 )怎么算,于是她找到了你。

【思路】
整理一下:
八连通(8个方向)
空格:八连通无雷
数字:八连通雷的数量
空:说白了就是,连起来的「空」,就和「孤岛」那一系列题中的岛屿或者海洋一样,类似于求海洋的个数或者岛屿的个数
个人感觉主要难点在于对「空」的理解,我读题的时候,理解了好久的「空」到底是个什么含义
求:求「空」的个数 + 周围没有「空」且当前位置是「数字」的个数
【代码】
cpp
/* 2026-04-01-02-算法打卡day36-DFS专项训练3
* 16-洛谷P4961-小埋与扫雷
* Author:郑龙浩
* Date:2026-04-02
* 算法:DFS
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000 + 10;
int n, m;
int grid[N][N] = {0};
bool visited[N][N] = {false};
int Grid[N][N] = {0};
int direction[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, -1}, {-1, 1}, {-1, -1}, {1, 1}};
// 将所有与传入的xy相连的空白标记为true,也就是,标记为访问过
void dfs(int x, int y) {
visited[x][y] = true;
int nextX, nextY;
for (int i = 0; i < 8; i++) {
nextX = x + direction[i][0];
nextY = y + direction[i][1];
if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= m) continue;
if (Grid[nextX][nextY] == 0 && visited[nextX][nextY] == false)
dfs(nextX, nextY);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) cin >> grid[i][j];
// 1 计算雷数
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) {Grid[i][j] = -1; continue;} // 跳过雷,雷设为-1
// 计算无雷位置周围有多少个雷
int nextX, nextY;
for (int k = 0; k < 8; k++) {
nextX = i + direction[k][0];
nextY = j + direction[k][1];
if (nextX < 0 || nextX >= n || nextY < 0|| nextY >= m) continue;
Grid[i][j] += grid[nextX][nextY];
}
}
// for (int i = 0; i < n; i++) {
// for (int j = 0; j < m; j++) cout << Grid[i][j] << ' ';
// cout << '\n';
// }
int cnt = 0;
// 2 计算「周围8个没有空格(是雷和数字) 且 当前位置是数字」的个数
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) {
int nextX, nextY;
if (Grid[i][j] == 0 || Grid[i][j] == -1) continue; // 必须保证当前位置是个数字,如果不是数字,则直接跳过当前位置的判断
bool f = true; // 假设当前位置是符合条件的
for (int k = 0; k < 8; k++) {
nextX = i + direction[k][0];
nextY = j + direction[k][1];
// 越界不妨问
if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= m) continue;
// 如果周围位置有任何一个空格,不可能再符合条件
if (Grid[nextX][nextY] == 0) {f = false; break;}
}
if (f) cnt++; // 如果是符合「周围8个没有空格的数字」这个条件的数字,就计数
}
// 3 计算有多少个「空」,也就是连起来的「空格」算1个空,计算数量
for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) {
if (Grid[i][j] == 0 && visited[i][j] == false) {
dfs(i, j);
cnt++;
}
}
cout << cnt;
return 0;
}
17-洛谷P1149-火柴棒等式
【题目】
题目描述
给你 n n n 根火柴棍,你可以拼出多少个形如 A + B = C A+B=C A+B=C 的等式?等式中的 A A A、 B B B、 C C C 是用火柴棍拼出的整数(若该数非零,则最高位不能是 0 0 0)。用火柴棍拼数字 0 ∼ 9 0\sim9 0∼9 的拼法如图所示:

注意:
- 加号与等号各自需要两根火柴棍;
- 如果 A ≠ B A\neq B A=B,则 A + B = C A+B=C A+B=C 与 B + A = C B+A=C B+A=C 视为不同的等式( A , B , C ≥ 0 A,B,C\geq0 A,B,C≥0);
- n n n 根火柴棍必须全部用上。
输入格式
一个整数 n ( 1 ≤ n ≤ 24 ) n(1 \leq n\leq 24) n(1≤n≤24)。
输出格式
一个整数,能拼成的不同等式的数目。
输入输出样例 #1
输入 #1
14
输出 #1
2
输入输出样例 #2
输入 #2
18
输出 #2
9
说明/提示
【输入输出样例 1 解释】
2 2 2 个等式为 0 + 1 = 1 0+1=1 0+1=1 和 1 + 0 = 1 1+0=1 1+0=1。
【输入输出样例 2 解释】
9 9 9 个等式为
0 + 4 = 4 0+4=4 0+4=4、 0 + 11 = 11 0+11=11 0+11=11、 1 + 10 = 11 1+10=11 1+10=11、 2 + 2 = 4 2+2=4 2+2=4、 2 + 7 = 9 2+7=9 2+7=9、 4 + 0 = 4 4+0=4 4+0=4、 7 + 2 = 9 7+2=9 7+2=9、 10 + 1 = 11 10+1=11 10+1=11、 11 + 0 = 11 11+0=11 11+0=11。
noip2008 提高第二题
【思路】
捋一下思路:
-
首先
数字:
0 1 2 3 4 5 6 7 8 9
分别对应火柴数量:
6 2 5 5 4 5 6 3 7 6 -
那么我就可以根据火柴数量去找对应的数字了:
- 刚开始我想的挺简单的,我以为一个火柴数量对应一个数字,直接写一个map映射就好了
- 然后我数了一下,反向同等数量的火柴可以拼出不同的数字,
- 那这个映射我就要改一下了:一个火柴数量可以映射出1个和多个数字 ,如下:
6个火柴可以拼出3个数字,2拼1个,5拼3个,3拼1个,7拼1个
-
2根火柴组成的:1
-
4根火柴组成的:4
-
5根火柴组成的:2 3 5
-
6根火柴组成的:0 6 9
-
7根火柴组成的:8
题目条件:
- 加号与等号各自需要两根火柴棍;
- 如果 A=B,则 A+B=C 与 B+A=C 视为不同的等式(A,B,C≥0);
- n 根火柴棍必须全部用上。
那么输入n之后,我直接减去4,也就是实际消耗是n-4,如果为负数,可以直接输出0了
整理了一下,大题思路就是,根据火柴数量,去遍历所有可能组成的数字:
写一个二维数组,一维索引分别对应2根火柴,4根火柴,...7根火柴
二维代表对应的能组成数字,宽度根据能组成的数字而定
思路错了,不应该根据火柴去拼数字,应该反过来想根据数字,去拼火柴 :
重新捋一下:
n的范围是1~24,去掉+和=的火柴数,也就是最多20个
然后需要拼3个数字,那么单个数字最多4位数,也就是我只需要尝试所有的四位数就好了,假设就是最大2000,随便设置的,如果还不行,那就再加数字测试
假设我的数字范围是1~2000,那么就不断尝试就好了
- 首先,计算出1~2000所有数字需要用到的火柴数
- 然后,不断尝试n-4,需要用到的火柴数量的所有可能,如果找到一个就cnt++,最后输出即可
绝对不可能组成数字的情况:
- 加号和等号共需4个火柴
- a + b = c,最少的情况,假设每个位置都是1,再给加个3吧
- 就按照小于等于 4 + 3 ,7个火柴的时候,直接输出0
- 当然不准确,具体最小的情况懒得用人脑去想了,直接交给计算机好了,数据量也不大
【代码】
cpp
/* 2026-04-01-02-算法打卡day36-DFS专项训练3
* 17-洛谷P1149-火柴棒等式
* Author:郑龙浩
* Date:2026-04-02
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int numCnt[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
// 返回数字num需要的火彩数量
int Num(int num) {
if (num == 0) return 6; // 刚开始忘记写这个了
int cnt = 0;
while (num) {
cnt += numCnt[num % 10];
num /= 10;
}
return cnt;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n; cin >> n;
if (n <= 7) {cout << 0; return 0;}
int numCnt2[2001] = {0};
// 计算出1 ~ 2000所需的火柴数量
for (int i = 0; i <= 2000; i++) {
numCnt2[i] = Num(i);
}
n -= 4;
int cnt = 0;
for (int i = 0; i <= 2000; i++) {
if (numCnt2[i] >= n) continue; // 如果火柴数量超了,就尝试下一组的数据
for (int j = 0; j <= 2000; j++) {
if (numCnt2[i] + numCnt2[j] >= n) continue; // 如果火柴数量超了,就尝试下一组
int c = i + j;
if (c > 2000) continue; // 如果c大于我设置的限制,也要尝试下一组,因为c如果大于2000,我是没有计算其火柴数量的
if (numCnt2[i] + numCnt2[j] + numCnt2[c] == n) cnt++;
}
}
cout << cnt;
return 0;
}