1

GESP认证C++编程真题解析 | 202409 八级

​欢迎大家订阅我的专栏:算法题解:C++与Python实现
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!

专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。

适合人群:

  • 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
  • 希望系统学习C++/Python编程的初学者
  • 想要提升算法与编程能力的编程爱好者

附上汇总帖:GESP认证C++编程真题解析 | 汇总


编程题

P11250 手套配对

【题目来源】

洛谷:P11250 [GESP202409 八级] 手套配对 - 洛谷

【题目描述】

小杨有 \(n\) 对不同的手套,每对手套由左右各一只组成。

小杨想知道从中取出 \(m\) 只手套,\(m\) 只手套恰好包含 \(k\) 对手套的情况有多少种。

小杨认为两种取出的情况不同,当且仅当两种情况取出的手套中存在不同的手套(同一对手套的左右手也视为不同的手套)。

【输入】

本题单个测试点内有多组测试数据

第一行包含一个正整数 \(t\),代表测试用例组数。

接下来是 \(t\) 组测试用例。对于每组测试用例,一共一行。

第一行包含三个正整数 \(n,m,k\),代表手套数量,取出的手套数和目标对数。

【输出】

对于每组测试数据,输出一个整数,代表可能的情况数量对 \(10^9+7\) 取模的结果。

【输入样例】

2
5 6 2
5 1 5

【输出样例】

120
0

【算法标签】

《洛谷 P11250 手套配对》 #组合数学# #排列组合# #GESP# #2024#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

#define int long long  // 将int定义为long long类型
const int N = 1005, mod = 1e9 + 7;

int t;               // 测试用例数量
int n, m, k, ans;    // n: 总数, m: 选择数量, k: 特定对数, ans: 答案
int c[N][N];         // 组合数C(n, m)数组
int d[N];            // 2的幂次数组

// 初始化组合数C(n, m) = C(n-1, m-1) + C(n-1, m)
void init()
{
    c[0][0] = 1;  // C(0,0) = 1
  
    for (int i = 1; i <= 1000; i++)
    {
        for (int j = 0; j <= i; j++)
        {
            if (j == 0)  // C(i,0) = 1
            {
                c[i][j] = 1;
            }
            else
            {
                // 组合数递推公式
                c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
            }
        }
    }
}

// 初始化2的幂次数组
void init2()
{
    d[0] = 1;  // 2^0 = 1
  
    for (int i = 1; i <= 1000; i++)
    {
        d[i] = d[i - 1] * 2 % mod;  // 2^i = 2^(i-1) * 2
    }
}

signed main()  // 由于定义了#define int long long,需要使用signed main
{
    cin >> t;
  
    // 预处理组合数和2的幂
    init();
    init2();
  
    while (t--)
    {
        cin >> n >> m >> k;
      
        // 条件检查:必须满足的基本限制
        // 1. m >= 2k: 至少需要2k个元素来形成k对
        // 2. m - 2k <= n - k: 剩余选择的元素不能超过可用的元素
        if (m < 2 * k || m - 2 * k > n - k)
        {
            cout << 0 << endl;
        }
        else
        {
            // 计算答案公式:C(n,k) * C(n-k, m-2k) * 2^(m-2k)
            int ans = ((c[n][k] * c[n - k][m - 2 * k]) % mod * d[m - 2 * k]) % mod;
            cout << ans << endl;
        }
    }
  
    return 0;
}

【运行结果】

2
5 6 2
120
5 1 5
0

P11251 美丽路径

【题目来源】

洛谷:P11251 [GESP202409 八级] 美丽路径 - 洛谷

【题目描述】

小杨有一棵包含 \(n\) 个节点的树,节点从 \(1\)\(n\) 编号,并且每个节点要么是白色,要么是黑色。

对于树上的一条简单路径(不经过重复节点的路径),小杨认为它是美丽的当且仅当路径上相邻节点的颜色均不相同。例如下图,其中节点 \(1\) 和节点 \(4\) 是黑色,其余节点是白色,路径 \(2-1-3-4\) 是美丽路径,而路径 \(2-1-3-5\) 不是美丽路径(相邻节点 \(3\)\(5\) 颜色相同)。

image

对于树上的一条简单路径,小杨认为它的长度是路径包含节点的数量。小杨想知道最长的美丽路径的长度是多少。

【输入】

第一行包含一个正整数 \(n\),代表节点数量。

第二行包含 \(n\) 个整数 \(c_1,c_2,\dots,c_n\),代表每个节点的颜色,如果 \(c_i=0\),代表节点 \(i\) 为白色,如果 \(c_i=1\),代表节点 \(i\) 为黑色。

之后 \(n-1\) 行,每行包含两个正整数 \(u_i,v_i\),代表存在一条连接节点 \(u_i\) 和节点 \(v_i\) 的边。

【输出】

输出一个整数,代表最长美丽路径的长度。

【输入样例】

5
1 0 0 1 0
1 2
3 5
4 3
1 3

【输出样例】

4

【算法标签】

《洛谷 P11251 美丽路径》 #动态规划DP# #GESP# #2024#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 100005, M = N * 2;

int n;              // 节点数量
int c[N];           // c[i] 表示节点i的颜色
int f1[N];          // f1[i]: 以节点i为起点的最长同色路径长度
int f2[N];          // f2[i]: 以节点i为起点的次长同色路径长度
int ans;            // 最终答案(路径上的节点数-1,即最大长度)
int h[N], e[M], ne[M], idx;  // 邻接表存储树

// 添加无向边
void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

// 深度优先搜索
// u: 当前节点
// fa: 父节点
void dfs(int u, int fa)
{
    // 遍历所有邻接节点
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];  // 邻接节点
        if (j == fa)   // 跳过父节点
        {
            continue;
        }
      
        // 递归处理子树
        dfs(j, u);
      
        // 计算从当前节点u到子节点j的路径长度
        // 如果u和j颜色不同,路径长度为f1[j]+1,否则为0
        int t = (c[u] != c[j] ? f1[j] + 1 : 0);
      
        // 更新f1[u]和f2[u]
        if (t > f1[u])
        {
            f2[u] = f1[u];  // 原来的最大值变为次大值
            f1[u] = t;      // 更新最大值
        }
        else if (t > f2[u])
        {
            f2[u] = t;      // 更新次大值
        }
    }
  
    // 更新答案:考虑经过节点u的最长同色路径
    // 注意:这里计算的是路径上的边数,而不是节点数
    ans = max(ans, f1[u] + f2[u]);
}

int main()
{
    cin >> n;
  
    // 初始化邻接表
    memset(h, -1, sizeof(h));
    idx = 0;
  
    // 读取每个节点的颜色
    for (int i = 1; i <= n; i++)
    {
        cin >> c[i];
    }
  
    // 读取树的边
    for (int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
  
    // 从节点1开始DFS
    dfs(1, 0);
  
    // 输出结果:最长同色路径的节点数 = 边数 + 1
    cout << ans + 1 << endl;
  
    return 0;
}

【运行结果】

5
1 0 0 1 0
1 2
3 5
4 3
1 3
4
posted @ 2026-01-18 00:19  热爱编程的通信人  阅读(2)  评论(0)    收藏  举报