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++六级模板

按照近年真题的考频情况,可以把模板分成:

第一梯队——★★★★★

  1. 一维 DP
  2. 选/不选 DP
  3. 划分 DP
  4. 前缀和 + DP
  5. vector 邻接表 + DFS
  6. vector 邻接表 + 子树大小
  7. vector 邻接表 + 树形 DP

第二梯队——★★★★☆

  1. 0/1 背包
  2. 完全背包
  3. BFS
  4. 二叉树后序 DFS
  5. 树上路径/距离
  6. 贪心 + 排序

第三梯队——★★★☆☆

  1. 二叉搜索树
  2. 树的奇偶层/二分染色
  3. 数论/最大因数

三、模板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) 作为树题的统一母模板。

posted @ 2026-10-02 10:36  kkman2000  阅读(0)  评论(0)    收藏  举报