CCF GESP C++ 6级编程题备考模板
CCF GESP C++ 六级编程题备考模板
重点方向:动态规划 + 树/图 + DFS/BFS + 前缀和 + 贪心 + 数论。
一、考情分析: GESP C++ 六级编程题汇总
| 考试时间 | 编程题1 | 主要算法 | 编程题2 | 主要算法 |
|---|---|---|---|---|
| 2024.06 | 计算得分 | 模拟/计算 | 二叉树 | 二叉树 |
| 2024.09 | 小杨和整数拆分 | DP | 算法学习 | 贪心/排序 |
| 2024.12 | 树上游走 | 树/DFS | 运送物资 | 树/DP |
| 2025.03 | 树上漫步 | 树/DFS | 环线 | DP/优化 |
| 2025.06 | 学习小组 | DP | 最大因数 | 数论/树 |
| 2025.09 | 划分字符串 | 字符串 DP | 货物运输 | 树/图 |
| 2025.12 | 路径覆盖 | 树形 DP | 道具商店 | DP/优化 |
| 2026.03 | 选数 | DP | 完全二叉树 | 二叉树/DFS |
| 2026.06 | 条形蛋糕 | 完全背包/DP | 满二叉树 | 树/DFS |
| 2026.09 | 数组划分 | 前缀和/DP | 分树规划 | 树/DFS |
二、CCF GESP C++六级模板
按照近年真题的考频情况,可以把模板分成:
第一梯队——★★★★★
- 一维 DP
- 选/不选 DP
- 划分 DP
- 前缀和 + DP
vector邻接表 + DFSvector邻接表 + 子树大小vector邻接表 + 树形 DP
第二梯队——★★★★☆
- 0/1 背包
- 完全背包
- BFS
- 二叉树后序 DFS
- 树上路径/距离
- 贪心 + 排序
第三梯队——★★★☆☆
- 二叉搜索树
- 树的奇偶层/二分染色
- 数论/最大因数
三、模板1:一维 DP
考频:★★★★★
CCF GESP C++ 六级最基础、最重要的 DP 模板之一。
典型:
dp[i] = 到第 i 个位置时的最优答案
通用模板
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> dp(n + 1);
dp[0] = 0;
for (int i = 1; i <= n; i++)
{
// 根据题目修改状态转移
dp[i] = dp[i - 1];
}
cout << dp[n] << endl;
return 0;
}
核心思想
dp[i]
↓
表示“做到第 i 个”的最优结果
↓
由前面的状态转移过来
四、模板2:选 / 不选 DP
考频:★★★★★
这是 GESP 六级非常常见的模型。
每个东西只有两种选择:
选
不选
模板
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> a(n + 1);
vector<int> dp(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
dp[0] = 0;
for (int i = 1; i <= n; i++)
{
// 不选 a[i]
dp[i] = dp[i - 1];
// 如果选择 a[i]
if (i >= 2)
{
dp[i] = max(dp[i], dp[i - 2] + a[i]);
}
}
cout << dp[n] << endl;
return 0;
}
看到这些关键词就要想到 DP
- 最多
- 最少
- 最大
- 最小
- 选择
- 不选择
- 相邻不能同时选
- 连续
- 最优方案
五、模板3:划分 DP
考频:★★★★★
六级后期越来越重要。
例如:
把一个序列划分成若干段,使得总代价最小。
通用模板
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> a(n + 1);
vector<int> dp(n + 1, INT_MAX);
for (int i = 1; i <= n; i++)
cin >> a[i];
dp[0] = 0;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < i; j++)
{
// [j+1, i] 作为最后一段
int cost = 0;
// 根据题目计算这一段的代价
dp[i] = min(dp[i], dp[j] + cost);
}
}
cout << dp[n] << endl;
return 0;
}
核心
dp[i]
=
前面已经划分好的部分
+
最后一段的代价
六、模板4:前缀和
考频:★★★★☆
虽然它不是 DP,但经常与 DP、区间问题组合出现。
模板
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
vector<long long> a(n + 1);
vector<long long> sum(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
}
// 求 [l, r] 的区间和
int l, r;
cin >> l >> r;
cout << sum[r] - sum[l - 1] << endl;
return 0;
}
必背公式
sum[i] = sum[i - 1] + a[i];
区间:
sum[r] - sum[l - 1]
2026.09 的“数组划分”就是前缀和与线性 DP 相结合的典型。
七、模板5:0/1 背包
考频:★★★★☆
每个物品:
最多只能选择一次
二维模板
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, V;
cin >> n >> V;
vector<int> w(n + 1);
vector<int> value(n + 1);
for (int i = 1; i <= n; i++)
cin >> w[i] >> value[i];
vector<vector<int>> dp(
n + 1,
vector<int>(V + 1, 0)
);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= V; j++)
{
// 不选
dp[i][j] = dp[i - 1][j];
// 选
if (j >= w[i])
{
dp[i][j] = max(
dp[i][j],
dp[i - 1][j - w[i]] + value[i]
);
}
}
}
cout << dp[n][V] << endl;
return 0;
}
八、模板6:完全背包 / 完全选择
考频:★★★★☆
每个物品可以使用很多次。
一维模板
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, V;
cin >> n >> V;
vector<int> w(n);
vector<int> value(n);
for (int i = 0; i < n; i++)
cin >> w[i] >> value[i];
vector<int> dp(V + 1, 0);
for (int i = 0; i < n; i++)
{
for (int j = w[i]; j <= V; j++)
{
dp[j] = max(
dp[j],
dp[j - w[i]] + value[i]
);
}
}
cout << dp[V] << endl;
return 0;
}
2026.06 的“条形蛋糕”就是经典的动态规划/完全背包类模型。
九、模板7:树——vector 邻接表 + DFS
考频:★★★★★
这是本次重点修改的部分。
以后 GESP 六级树题,建议统一使用:
vector<vector<int>> g;
不要再使用:
struct Edge
{
int to;
int next;
};
int head[];
7.1 无权树基本模板
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
void dfs(int u, int father)
{
for (int v : g[u])
{
if (v == father)
continue;
dfs(v, u);
}
}
int main()
{
int n;
cin >> n;
g.resize(n + 1);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
return 0;
}
最重要的三行
vector<vector<int>> g(n + 1);
g[u].push_back(v);
g[v].push_back(u);
因为树是:
u —— v
所以要建立:
u → v
v → u
十、模板8:vector 邻接表 + 统计子树大小
考频:★★★★★
这是六级非常值得背的树模板。
模板
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
vector<int> sz;
void dfs(int u, int father)
{
sz[u] = 1;
for (int v : g[u])
{
if (v == father)
continue;
dfs(v, u);
sz[u] += sz[v];
}
}
int main()
{
int n;
cin >> n;
g.resize(n + 1);
sz.resize(n + 1);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
for (int i = 1; i <= n; i++)
{
cout << "节点 " << i
<< " 的子树大小 = "
<< sz[i] << endl;
}
return 0;
}
一定要理解
sz[u] = 1;
代表:
节点 u 自己算 1 个。
然后:
sz[u] += sz[v];
把孩子的整个子树加进来。
十一、模板9:vector 邻接表 + 树形 DP
考频:★★★★★
这是六级重点中的重点。
2025.12 的“路径覆盖”就是典型树形 DP,核心状态可以写成:
dp[u] = min(c[u], 所有孩子 dp[v] 之和)
通用树形 DP 模板
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
vector<long long> dp;
vector<long long> cost;
void dfs(int u, int father)
{
// 方案1:直接选择 u
dp[u] = cost[u];
long long sum = 0;
for (int v : g[u])
{
if (v == father)
continue;
dfs(v, u);
sum += dp[v];
}
// 方案2:不选 u,解决所有子树
dp[u] = min(dp[u], sum);
}
int main()
{
int n;
cin >> n;
g.resize(n + 1);
dp.resize(n + 1);
cost.resize(n + 1);
for (int i = 1; i <= n; i++)
cin >> cost[i];
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
cout << dp[1] << endl;
return 0;
}
树形 DP 的核心结构
void dfs(int u, int father)
{
// 先处理自己
for (int v : g[u])
{
if (v == father)
continue;
dfs(v, u);
// 再利用孩子的信息
}
// 最后计算 dp[u]
}
这个结构一定要熟。
十二、模板10:带权树——vector 邻接表
考频:★★★★☆
如果边上有权值:
u —— v
w
可以直接:
vector<vector<pair<int, long long>>> g;
模板
#include <bits/stdc++.h>
using namespace std;
vector<vector<pair<int, long long>>> g;
void dfs(int u, int father, long long dist)
{
for (auto [v, w] : g[u])
{
if (v == father)
continue;
dfs(v, u, dist + w);
}
}
int main()
{
int n;
cin >> n;
g.resize(n + 1);
for (int i = 1; i < n; i++)
{
int u, v;
long long w;
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
dfs(1, 0, 0);
return 0;
}
遍历时
for (auto [v, w] : g[u])
其中:
v = 相邻节点
w = 边权
十三、模板11:求树的最大深度
考频:★★★★☆
二叉树、普通树都可以使用这个思路。
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
int maxDepth = 0;
void dfs(int u, int father, int depth)
{
maxDepth = max(maxDepth, depth);
for (int v : g[u])
{
if (v == father)
continue;
dfs(v, u, depth + 1);
}
}
int main()
{
int n;
cin >> n;
g.resize(n + 1);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0, 0);
cout << maxDepth << endl;
return 0;
}
十四、模板12: 奇偶层统计
考频:★★★★☆
2025.03 “树上漫步”就是典型的树 DFS / 奇偶层问题。
模板
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
int cnt[2];
void dfs(int u, int father, int depth)
{
cnt[depth % 2]++;
for (int v : g[u])
{
if (v == father)
continue;
dfs(v, u, depth + 1);
}
}
int main()
{
int n;
cin >> n;
g.resize(n + 1);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0, 0);
cout << cnt[0] << " "
<< cnt[1] << endl;
return 0;
}
十五、模板13: BFS
考频:★★★★☆
DFS 与 BFS 都应该掌握。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
vector<vector<int>> g(n + 1);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> dist(n + 1, -1);
queue<int> q;
dist[1] = 0;
q.push(1);
while (!q.empty())
{
int u = q.front();
q.pop();
for (int v : g[u])
{
if (dist[v] != -1)
continue;
dist[v] = dist[u] + 1;
q.push(v);
}
}
for (int i = 1; i <= n; i++)
{
cout << dist[i] << " ";
}
return 0;
}
BFS 三件套
queue<int> q;
dist[1] = 0;
q.push(1);
然后:
while (!q.empty())
不断取出节点。
十六、模板14:二叉树——后序 DFS
考频:★★★★☆
近两次考试出现了“完全二叉树”“满二叉树”等二叉树题目。
2026.06 的“满二叉树”需要通过后序 DFS 获得左右子树信息,并结合子树高度判断。
如果题目给的是普通树结构,可以直接:
vector<vector<int>> g;
通用后序模板
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
int dfs(int u, int father)
{
int height = 1;
for (int v : g[u])
{
if (v == father)
continue;
int childHeight = dfs(v, u);
height = max(height, childHeight + 1);
}
return height;
}
int main()
{
int n;
cin >> n;
g.resize(n + 1);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
cout << dfs(1, 0) << endl;
return 0;
}
十七、模板15:判断二叉树 / 满二叉树的 DFS 框架
考频:★★★★☆
这个模板重点不是死背代码,而是掌握:
先处理孩子
↓
得到孩子的信息
↓
再判断当前节点
例如:
struct Info
{
int height;
bool ok;
};
模板
Info dfs(int u, int father)
{
Info ans;
ans.height = 1;
ans.ok = true;
vector<int> child;
for (int v : g[u])
{
if (v == father)
continue;
child.push_back(v);
}
// 根据孩子数量判断
if (child.size() > 2)
{
ans.ok = false;
return ans;
}
for (int v : child)
{
Info t = dfs(v, u);
if (!t.ok)
ans.ok = false;
ans.height = max(
ans.height,
t.height + 1
);
}
return ans;
}
十八、模板16:二叉树的 vector 邻接表写法
如果输入明确告诉你:
每个节点有左孩子、右孩子
其实可以直接使用:
vector<vector<int>> g(n + 1);
例如:
for (int i = 1; i <= n; i++)
{
int l, r;
cin >> l >> r;
if (l != 0)
g[i].push_back(l);
if (r != 0)
g[i].push_back(r);
}
然后:
void dfs(int u)
{
for (int v : g[u])
{
dfs(v);
}
}
这样可以把:
普通树
和:
二叉树
统一成同一种思维:
节点
↓
邻接节点
↓
DFS
↓
孩子信息
↓
当前节点信息
十九、模板17:树上两点距离
考频:★★★☆☆~★★★★☆
如果树没有环,可以从一个点 DFS/BFS 到另一个点。
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
vector<int> dist;
void dfs(int u, int father)
{
for (int v : g[u])
{
if (v == father)
continue;
dist[v] = dist[u] + 1;
dfs(v, u);
}
}
int main()
{
int n, s, t;
cin >> n >> s >> t;
g.resize(n + 1);
dist.resize(n + 1, -1);
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dist[s] = 0;
dfs(s, 0);
cout << dist[t] << endl;
return 0;
}
二十、模板18:树的“父节点链”
考频:★★★☆☆
某些 GESP 题目会给:
parent[i]
也就是:
i 的父节点
这种情况不一定需要邻接表。
vector<int> parent(n + 1);
for (int i = 2; i <= n; i++)
{
cin >> parent[i];
}
向上走:
int x = u;
while (x != 0)
{
cout << x << " ";
x = parent[x];
}
2025.06“最大因数”涉及不断沿关系向上/变化的过程,这类题目可以结合数论与链式关系处理。
二十一、模板19:贪心 + 排序
考频:★★★☆☆~★★★★☆
2024.09“算法学习”属于典型的排序/贪心思路。题目核心包括按条件排序并选择尽可能少的对象。
基本模板
#include <bits/stdc++.h>
using namespace std;
struct Node
{
int a;
int b;
};
bool cmp(Node x, Node y)
{
return x.b > y.b;
}
int main()
{
int n;
cin >> n;
vector<Node> a(n);
for (int i = 0; i < n; i++)
{
cin >> a[i].a >> a[i].b;
}
sort(a.begin(), a.end(), cmp);
for (auto x : a)
{
cout << x.a << " "
<< x.b << endl;
}
return 0;
}
二十二、模板20:数论——最大因数
考频:★★★☆☆
六级会出现一些:
因数
约数
质数
最大因数
分解
求最大真因数
int maxFactor(int x)
{
for (int i = x / 2; i >= 1; i--)
{
if (x % i == 0)
return i;
}
return 1;
}
如果数据较大,可以进一步优化。
例如:
int maxFactor(int x)
{
for (int i = 2; i * i <= x; i++)
{
if (x % i == 0)
{
return x / i;
}
}
return 1;
}
二十三、“树题”统一模板
建议直接作为考试前背诵模板。
模板 A:普通树 DFS
vector<vector<int>> g;
void dfs(int u, int fa)
{
for (int v : g[u])
{
if (v == fa)
continue;
dfs(v, u);
}
}
考频
★★★★★
模板 B:统计子树大小
vector<vector<int>> g;
vector<int> sz;
void dfs(int u, int fa)
{
sz[u] = 1;
for (int v : g[u])
{
if (v == fa)
continue;
dfs(v, u);
sz[u] += sz[v];
}
}
考频
★★★★★
模板 C:树形 DP
vector<vector<int>> g;
vector<long long> dp;
void dfs(int u, int fa)
{
for (int v : g[u])
{
if (v == fa)
continue;
dfs(v, u);
// 使用 dp[v]
}
// 计算 dp[u]
}
考频
★★★★★
模板 D:树上深度
void dfs(int u, int fa, int dep)
{
for (int v : g[u])
{
if (v == fa)
continue;
dfs(v, u, dep + 1);
}
}
考频
★★★★☆
模板 E:树上奇偶层
int cnt[2];
void dfs(int u, int fa, int dep)
{
cnt[dep % 2]++;
for (int v : g[u])
{
if (v == fa)
continue;
dfs(v, u, dep + 1);
}
}
考频
★★★★☆
模板 F:树上距离
vector<int> dist;
void dfs(int u, int fa)
{
for (int v : g[u])
{
if (v == fa)
continue;
dist[v] = dist[u] + 1;
dfs(v, u);
}
}
考频
★★★★☆
二十四、vector 邻接表必须熟练掌握的三种写法
① 普通无权树
vector<vector<int>> g(n + 1);
g[u].push_back(v);
g[v].push_back(u);
② 带权树
vector<vector<pair<int, int>>> g(n + 1);
g[u].push_back({v, w});
g[v].push_back({u, w});
③ 有向图
vector<vector<int>> g(n + 1);
g[u].push_back(v);
注意:
g[v].push_back(u);
有向图一般不能随便加这一句。
二十五、GESP 六级最终模板清单
| 模板 | 核心代码 | 考频 | 优先级 |
|---|---|---|---|
| 一维 DP | dp[i] |
★★★★★ | 必背 |
| 选/不选 DP | max(dp[i-1],...) |
★★★★★ | 必背 |
| 划分 DP | dp[i] + 最后一段 |
★★★★★ | 必背 |
| 前缀和 | sum[i]=sum[i-1]+a[i] |
★★★★☆ | 必背 |
| 0/1 背包 | 物品只能一次 | ★★★★☆ | 必背 |
| 完全背包 | 物品可多次 | ★★★★☆ | 必背 |
| vector 邻接表 DFS | g[u].push_back(v) |
★★★★★ | 必背 |
| 子树大小 | sz[u]+=sz[v] |
★★★★★ | 必背 |
| 树形 DP | dfs(v,u)+dp[v] |
★★★★★ | 必背 |
| 树上深度 | dep+1 |
★★★★☆ | 必背 |
| 奇偶层 | dep%2 |
★★★★☆ | 必背 |
| BFS | queue<int> |
★★★★☆ | 必背 |
| 二叉树后序 | 先孩子、后父节点 | ★★★★☆ | 必背 |
| 带权树 | pair<int,int> |
★★★★☆ | 掌握 |
| 树上距离 | dist[v]=dist[u]+1 |
★★★★☆ | 掌握 |
| 贪心排序 | sort() |
★★★★☆ | 掌握 |
| 数论 | 因数/质数 | ★★★☆☆ | 掌握 |
| BST | 插入/查找/中序 | ★★★☆☆ | 了解 |
二十六、建议六级考前重点背这 8 个模板
如果时间有限,不需要把 20 个模板全部死背。
第一组:DP
① 一维 DP
② 选/不选 DP
③ 划分 DP
④ 0/1 / 完全背包
第二组:树
⑤ vector 邻接表 + DFS
⑥ vector 邻接表 + 子树大小
⑦ vector 邻接表 + 树形 DP
⑧ vector 邻接表 + BFS
其中最重要的树模板可以压缩成这一份:
#include <bits/stdc++.h>
using namespace std;
vector<vector<int>> g;
vector<int> sz;
vector<long long> dp;
void dfs(int u, int fa)
{
// 1. 初始化当前节点
sz[u] = 1;
// 2. 遍历所有孩子
for (int v : g[u])
{
// 3. 不走回父亲
if (v == fa)
continue;
// 4. 先处理孩子
dfs(v, u);
// 5. 使用孩子的信息
sz[u] += sz[v];
// dp[u] = 根据 dp[v] 转移
}
// 6. 最后处理当前节点
}
int main()
{
int n;
cin >> n;
// vector 邻接表
g.resize(n + 1);
sz.resize(n + 1);
dp.resize(n + 1);
// n-1 条边
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
// 从 1 号节点开始
dfs(1, 0);
return 0;
}
这一份建议重点背熟。
因为它可以继续改造成:
普通树 DFS
↓
求深度
↓
求子树大小
↓
求叶子数量
↓
统计奇偶层
↓
求树的直径
↓
树形 DP
↓
树上最优解
而且从近年的真题看,树相关题目的覆盖非常持续;2024.12、2025.03、2025.06、2025.09、2025.12、2026.03、2026.06、2026.09 都能看到树、二叉树或树形结构相关考点。
备考 GESP C++ 六级时,把 vector<vector<int>> g + DFS(u, fa) 作为树题的统一母模板。

浙公网安备 33010602011771号