2025牛客寒假算法营3

L题

cpp 复制代码
#include<bits/stdc++.h>


using namespace std;


const int MAXN = 100005;


// 定义一个邻接表 G 来表示图
// G[i] 存储与节点 i 相邻的所有边的信息
// 每个边信息用 pair<int, int> 表示,第一个元素是相邻节点的编号,第二个元素是该边的唯一编号
vector<pair<int, int>> G[MAXN]; 

bool vis[MAXN];
// 布尔类型数组 vis,用于标记每条边是否已经被访问过
// 初始值默认为 false,当边被访问后设为 true

int n, b[MAXN], tot;
// n 表示输入的三角形层数,由用户输入决定
// b 数组用于辅助计算每个节点的编号
// tot 是一个计数器,用于给每条边分配唯一的编号

vector<int> ans;
// 存储最终的一笔画顶点序列,即欧拉路径

// 向图中添加无向边的函数
// u 和 v 分别表示边的两个端点
void add_edge(int u, int v)
{
    // 在节点 u 的邻接表中添加一条指向节点 v 的边
    // 边的编号为 tot,同时使用 emplace_back 直接在原地构造元素,提高效率
    G[u].emplace_back(v, tot); 
    // 在节点 v 的邻接表中添加一条指向节点 u 的边
    // 由于是无向边,所以边的编号与上面添加的边相同
    // 添加完边后,将 tot 的值加 1,为下一条边分配新的编号
    G[v].emplace_back(u, tot++); 
}

// 深度优先搜索函数,用于寻找欧拉路径
// x 表示当前正在访问的节点
void dfs(int x)
{
    // 遍历与节点 x 相邻的所有边
    for (auto &i: G[x]) 
    {
        // 如果该边已经被访问过,则跳过这条边,继续检查下一条边
        if (vis[i.second]) continue; 
        // 标记这条边为已访问
        vis[i.second] = true; 
        // 递归调用 dfs 函数,从相邻节点 i.first 继续进行深度优先搜索
        dfs(i.first); 
    }
    // 当从节点 x 出发的所有边都被访问完后
    // 将节点 x 添加到结果序列 ans 中,这是回溯过程
    ans.push_back(x); 
}

int main()
{
    // 从标准输入读取一个整数 n,表示三角形的层数
    scanf("%d", &n); 

    // 初始化 b 数组的第一个元素为 1
    // b 数组用于计算每个节点的编号,后续会根据规律更新
    b[0] = 1; 

    // 计算 b 数组的值
    // b[i] 表示第 i 层第一个节点的编号
    // 第 i 层的第一个节点编号等于第 i - 1 层第一个节点编号加上 i
    for (int i = 1; i <= n; ++i)
    {
        b[i] = b[i - 1] + i;
    }

    // 构建图的边
    // 对于每一层的每个三角形,连接其三个顶点形成边
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j <= i; ++j)
        {
            // 计算当前三角形的三个顶点编号
            // p1 是第 i 层第 j 个节点的编号
            // p2 是第 i + 1 层第 j 个节点的编号
            // p3 是第 i + 1 层第 j + 1 个节点的编号
            int p1 = b[i] + j, p2 = b[i + 1] + j, p3 = b[i + 1] + j + 1; 

            // 添加边 (p1, p2)
            add_edge(p1, p2); 
            // 添加边 (p2, p3)
            add_edge(p2, p3); 
            // 添加边 (p3, p1)
            add_edge(p3, p1); 
        }
    }

    // 从节点 1 开始进行深度优先搜索
    // 因为本题构建的图一定存在欧拉回路,所以可以从任意节点开始搜索,这里选择节点 1
    dfs(1); 

    // 输出结果标志,表示存在一笔画方案
    printf("Yes\n"); 

    // 输出最终的一笔画顶点序列
    for (int i = 0; i < ans.size(); ++i)
    {
        // 输出节点编号
        printf("%d", ans[i]); 
        // 如果是最后一个节点,输出换行符;否则输出空格
        printf("%c", " \n"[i + 1 == ans.size()]); 
    }

    return 0;
}



C题

cpp 复制代码
#include<bits/stdc++.h>


using namespace std;

const int MAXN = 100005;

string a[MAXN];
// 定义一个字符串数组 a,用于存储输入的所有单词

int n, m;
// n 表示单词的数目,m 表示查询的数目

// 计算两个字符串的最长公共前缀长度
int lcp(const string &A, const string &B)
{
    int i = 0;
    // 当 i 小于两个字符串的长度且对应位置字符相等时,i 自增
    while (i < A.size() && i < B.size() && A[i] == B[i]) ++i;
    return i;
    // 返回最长公共前缀的长度
}

int main(int argc, char *argv[])
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    

    cin >> n >> m;
    // 从标准输入读取单词的数目 n 和查询的数目 m

    for (int i = 0; i < n; ++i)
    {
        cin >> a[i];
     
    }

    sort(a, a + n);
    // 对存储的单词按字典序进行排序,这样相邻单词的公共前缀更有可能较长,便于后续计算

    int mx = 0, sum = 0;
    // mx 用于记录最长单词的长度,sum 用于记录总的按键次数

    for (int i = 0; i < n; ++i)
    {
        sum += (int) a[i].size() * 2;
        // 先假设每次输入一个新单词都需要完整输入该单词,再完整删除该单词,所以乘以 2

        if (i) sum -= lcp(a[i], a[i - 1]) * 2;
        // 如果不是第一个单词,计算当前单词和前一个单词的最长公共前缀长度
        // 因为公共前缀部分不需要重复输入和删除,所以减去公共前缀长度的两倍

        mx = max(mx, (int) a[i].size());
        // 更新最长单词的长度
    }

    cout << sum - mx << endl;
    // 最终结果为总的按键次数减去最长单词的长度
    // 因为最后一个单词输入后不需要删除,所以减去最长单词的长度得到最少按键次数

    return 0;
}



E题

cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>

using namespace std;

// 定义一个极大值 INF,用于二分查找的右边界
const int INF = 1000000001;

// 计算在时间 t 内发生的碰撞对数
long long count_pairs(long long t, vector<long long> &u, vector<long long> &v)
{
    long long result = 0;
    // p1 和 p2 是两个指针,用于遍历 v 数组
    int p1 = 0, p2 = 0;
    // 遍历所有向右运动的小球的初始位置
    for (auto &i: u)
    {
        // 移动 p2 指针,使其指向第一个大于等于 i 的位置
        while (p2 < v.size() && v[p2] < i) ++p2;
        // 移动 p1 指针,使其指向第一个大于 i + t 的位置
        while (p1 < v.size() && v[p1] <= i + t) ++p1;
        // 计算在时间 t 内,当前向右运动的小球与向左运动的小球的碰撞对数
        result += p1 - p2;
    }
    return result;
}

int main()
{
    int n;
    long long k;

    // 读取小球的数量 n 和要查询的碰撞对数 k
    scanf("%d %lld", &n, &k);
    // 存储每个小球的初始位置和速度
    vector<pair<long long, long long>> a(n); 
    // 存储向右运动的小球的初始位置
    vector<long long> u; 
    // 存储向左运动的小球的初始位置
    vector<long long> v; 
    for (int i = 0; i < n; ++i)
    {
        long long x, y;
        // 读取每个小球的初始位置 x 和速度 y
        scanf("%lld %lld", &x, &y);
        if (y == 1)
        {
            // 如果速度为 1,表示向右运动,将其初始位置加入 u 数组
            u.push_back(x);
        }
        else
        {
            // 如果速度为 -1,表示向左运动,将其初始位置加入 v 数组
            v.push_back(x);
        }
    }

    // 对向右运动的小球的初始位置进行排序
    sort(u.begin(), u.end());
    // 对向左运动的小球的初始位置进行排序
    sort(v.begin(), v.end());

    // 二分查找的左边界
    int l = 0; 
    // 二分查找的右边界
    int r = INF; 
    // 记录满足条件的最小时间
    int ans = 0; 
    while (l <= r)
    {
        // 计算中间时间
        int mid = (l + r) / 2; 
        // 计算在时间 mid 内发生的碰撞对数
        auto nowk = count_pairs(mid, u, v); 
        if (nowk >= k)
        {
            // 如果碰撞对数大于等于 k,说明时间可能过长,缩小右边界
            r = mid - 1;
        }
        else
        {
            // 如果碰撞对数小于 k,说明时间过短,更新 ans 并扩大左边界
            ans = mid;
            l = mid + 1;
        }
    }

    if (ans == INF)
    {
        // 如果 ans 仍然等于 INF,说明无法达到第 k 对碰撞,输出 No
        printf("No\n");
        return 0;
    }
    ans++;
    // 输出 Yes,并格式化输出达到第 k 对碰撞的时间
    printf("Yes\n%d.%c00000\n", ans / 2, "05"[ans & 1]);
    return 0;
}
相关推荐
纠结哥_Shrek7 分钟前
独立成分分析 (ICA):用于信号分离或降维
人工智能·python·算法
gentle_ice17 分钟前
leetcode——二叉树的中序遍历(java)
java·数据结构·算法·leetcode
吃一口大米饭43 分钟前
合并两个有序链表(leetcode刷题)
java·数据结构·算法·leetcode·链表
0x7F7F7F7F2 小时前
图论——最小生成树
图论
Big David2 小时前
机器人抓取与操作经典规划算法(深蓝)——2
算法·机器人·具身智能
0x7F7F7F7F2 小时前
图论——最小生成树的扩展应用
图论
灰末3 小时前
Forsaken喜欢数论(线性筛)
算法
gentle_ice4 小时前
leetcode——二叉树的最大深度(java)
java·数据结构·算法·leetcode
咒法师无翅鱼4 小时前
【leetcode】T1599
算法·leetcode·职场和发展
i_kmling4 小时前
Leetcode 119. 杨辉三角 II
c++·python·算法·leetcode