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;
}
相关推荐
沐怡旸1 小时前
【算法】【链表】328.奇偶链表--通俗讲解
算法·面试
掘金安东尼4 小时前
Amazon Lambda + API Gateway 实战,无服务器架构入门
算法·架构
码流之上5 小时前
【一看就会一写就废 指间算法】设计电子表格 —— 哈希表、字符串处理
javascript·算法
快手技术7 小时前
快手提出端到端生成式搜索框架 OneSearch,让搜索“一步到位”!
算法
CoovallyAIHub1 天前
中科大DSAI Lab团队多篇论文入选ICCV 2025,推动三维视觉与泛化感知技术突破
深度学习·算法·计算机视觉
NAGNIP1 天前
Serverless 架构下的大模型框架落地实践
算法·架构
moonlifesudo1 天前
半开区间和开区间的两个二分模版
算法
moonlifesudo1 天前
300:最长递增子序列
算法
CoovallyAIHub1 天前
港大&字节重磅发布DanceGRPO:突破视觉生成RLHF瓶颈,多项任务性能提升超180%!
深度学习·算法·计算机视觉
CoovallyAIHub1 天前
英伟达ViPE重磅发布!解决3D感知难题,SLAM+深度学习完美融合(附带数据集下载地址)
深度学习·算法·计算机视觉