跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

洛谷六题算法记录:模幂、构造、背包、贪心、剪枝、堆维护

这篇记录整理了 6 道洛谷典型题,覆盖快速幂取模、图构造、差值转化后的 01 背包、按耗时排序的贪心、DFS 暴搜配合剪枝,以及用小根堆维护多水龙头的最早结束时间。每题都给出了核心思路和对应 C++ 实现,重点放在算法转化、合法性判断和状态维护上,适合用来串起常见竞赛模板的实际用法。

XiaoPingzi发布于 2026/6/30更新于 2026/10/2670 浏览
洛谷六题算法记录:模幂、构造、背包、贪心、剪枝、堆维护

在这里插入图片描述

这组题都来自洛谷,题型很典型,适合拿来练手。它们不算那种靠灵感硬冲的题,更多是把算法模板真正用到题目里:模幂、构造图、状态转化后的背包、贪心排序、DFS 剪枝、以及用小根堆维护最小结束时间。

在这里插入图片描述

题目清单

1. 转圈游戏

在这里插入图片描述

题目: P1965 [NOIP 2013 提高组] 转圈游戏

在这里插入图片描述

解法:数学 + 快速幂

题意其实很直接:一圈 n 个数,移动后的位置可以写成 (x + m * 10^k) % n。麻烦点不在公式,而在 10^k 很大,不能老老实实算,只能做取模快速幂。

这题就是标准的'会公式不难,卡在幂太大'。long long 足够用,边乘边取模就行。

代码:

#include <iostream>
using namespace std;
typedef long long LL;
LL n, m, k, x;

LL qpow(LL a, LL b, LL p) {
    LL ret = 1;
    while (b) {
        if (b & 1) ret = ret * a % p;
        b >>= 1;
        a = a * a % p;
    }
     ret;
}

{
    cin >> n >> m >> k >> x;
    cout << (x + m * (, k, n)) % n << endl;
     ;
}
return
int main()
qpow
10
return
0

2. System Administrator

题目: CF22C System Administrator

在这里插入图片描述

解法:构造

这题的核心不是'怎么连',而是先判断有没有解。要满足'删掉一个点后图不连通',最自然的办法是让这个点 v 连接一部分点,剩下的点自己形成另一个连通块。这样删掉 v 以后,图就断开了。

边数也要卡住范围:

  • 连通图至少需要 n - 1 条边,所以 m < n - 1 肯定无解;
  • 另一方面,要在这个结构下尽量多连边,能达到的上界是 (n - 1) * (n - 2) / 2 + 1。

也就是说,只有当 n - 1 < m < (n - 1) * (n - 2) / 2 + 1 时,才有构造空间。剩下的事就比较机械了:先把 v 和其他点连起来,再在不碰 v 的前提下补边,把剩余边数用完。

代码:

#include <iostream>
using namespace std;
typedef long long LL;
LL n, m, v;

int main() {
    cin >> n >> m >> v;
    if (m < n - 1 || m > (n - 1) * (n - 2) / 2 + 1) {
        cout << -1 << endl;
        return 0;
    }
    for (int i = 1; i <= n; i++) {
        if (i != v) {
            cout << i << " " << v << endl;//v 点和其它点相连
            m--;
        }
    }
    int w = 1;
    if (w == v) w = 2;//选一个不是 v 的点
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j < i; j++)//i 与 j 不同,只连一次
        {
            if (!m) return 0;
            if (i == v || i == w || j == v || j == w) continue;
            cout << i << " " << j << endl;
            m--;
        }
    }
    return 0;
}

在这里插入图片描述

3. 多米诺骨牌

在这里插入图片描述

题目: P1282 多米诺骨牌

在这里插入图片描述

解法:动态规划(01 背包)

这题不难,难点在转化。直接处理上下两排数字和翻转操作,会把状态搞得很乱;换个思路,先看每个骨牌的上下差值。这样'翻转'就只是把差值取反,问题一下变成了:每张牌选或不选,最后让总差值尽量接近 0,同时旋转次数尽量少。

有一个现实问题也得处理:差值可能是负数,不能直接拿去做数组下标。做法很常见,给第二维整体加一个偏移量,把可取范围平移到正数区间里。

这里设 f[i][j] 表示前 i 张骨牌,差值为 j 时的最少翻转次数。转移时分两种情况:不翻转,差值贡献是 a[i];翻转,贡献是 -a[i],翻转次数加 1。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int OFFSET = 5000;
const int INF = 0x3f3f3f3f;
int f[1005][10005];
int a[1005];

int main() {
    int n;
    cin >> n;
    memset(f, 0x3f, sizeof(f));
    f[0][OFFSET] = 0;
    for (int i = 1; i <= n; ++i) {
        int u, d;
        cin >> u >> d;
        a[i] = u - d;
        for (int j = 0; j <= 10000; ++j) {
            if (j - a[i] >= 0 && j - a[i] <= 10000)
                f[i][j] = min(f[i][j], f[i - 1][j - a[i]]);
            if (j + a[i] >= 0 && j + a[i] <= 10000)
                f[i][j] = min(f[i][j], f[i - 1][j + a[i]] + 1);
        }
    }
    int ans = INF, best_j = 0;
    for (int j = 0; j <= 10000; ++j) {
        if (f[n][j] < ans) {
            ans = f[n][j];
            best_j = j;
        }
    }
    cout << ans << endl;
    return 0;
}

4. 排队接水

在这里插入图片描述

题目: P1223 排队接水

在这里插入图片描述

解法:贪心

这题按接水时间从小到大排就够了。短任务先做,后面人的等待时间会被压下来;长任务放后面,整体平均等待时间更小。思路很朴素,也很稳。

写的时候只要维护一个 wait,记录当前累计等了多久,再顺手累加到总等待时间里。

代码:

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e3 + 10;
int n;
struct node {
    int t, id;
} a[N];
bool cmp(node& x, node& y) {
    return x.t < y.t;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].t;
        a[i].id = i;
    }
    sort(a + 1, a + 1 + n, cmp);
    double sum = 0, wait = 0;
    for (int i = 1; i <= n; i++) {
        cout << a[i].id << " ";
        sum += wait;
        wait += a[i].t;
    }
    cout << endl;
    printf("%.2lf\n", sum / n);
    return 0;
}

5. 健康的荷斯坦奶牛

在这里插入图片描述

题目: P1460 [USACO2.1] 健康的荷斯坦奶牛 Healthy Holsteins

在这里插入图片描述

解法:DFS 暴搜 + 剪枝

这题本质上就是枚举选不选每一种饲料。饲料种类不多,直接 DFS 把所有组合走一遍是可行的;真正决定效率的是剪枝。

这里用了两个比较实用的剪枝:一是如果当前已经选的饲料数不可能优于已有答案,就直接停;二是如果当前组合已经满足需求,就更新答案,不必继续往下搜。顺序上按饲料编号从小到大枚举,所以第一次搜到的最优解天然更接近字典序,这点也挺省事。

实现上要记得回溯恢复现场,path 用位运算存当前选了哪些饲料,够轻也够直接。

代码:

#include <iostream>
using namespace std;
const int N = 30;
int n, m;
int v[N];
int g[N][N];
int cnt;//记录当前选了多少个
int path;//记录选了哪些饲料
int ret = N;//记录最少选了
int st;//当前的决策是 path 是否满足奶牛的需要

bool check() {
    for (int i = 1; i <= n; i++) {
        int sum = 0;
        for (int j = 1; j <= m; j++) {
            if ((path >> j) & 1) sum += g[j][i];
        }
        if (sum < v[i]) return false;
    }
    return true;
}

void dfs(int pos) {
    //最优性剪枝
    if (cnt >= ret) return;
    if (check()) {
        ret = cnt;
        st = path;
        return;
    }
    if (pos > m) return;
    //选
    cnt++;
    path |= (1 << pos);//置为 1
    dfs(pos + 1);
    cnt--;
    path &= ~(1 << pos);//不选
    dfs(pos + 1);
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> v[i];
    cin >> m;
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++) cin >> g[i][j];
    dfs(1);
    cout << ret << " ";
    for (int i = 1; i <= m; i++) {
        if ((st >> i) & 1) cout << i << " ";
    }
    return 0;
}

6. 接水问题

题目: P1190 [NOIP 2010 普及组] 接水问题

在这里插入图片描述

解题思路:模拟,小根堆

这题就是多水龙头并行接水,关键在于每次都能快速拿到'最早空出来的那个龙头'。用小根堆维护每个龙头的结束时间就很顺手:每来一个学生,弹出最小结束时间,加上他的接水时长,再放回去。最后堆里记录到的最大结束时间,就是总用时。

这种写法比手动模拟水龙头数组省心不少,代码也干净。

代码:

//模拟,小根堆,同时找 max
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int n, m;

int main() {
    cin >> n >> m;
    priority_queue<int, vector<int>, greater<int>> heap;
    for (int i = 1; i <= m; i++)//初始化 m 个水龙头的结束时间,方便后面 n 次,不用提前存
        heap.push(0);
    int ret = 0;
    for (int i = 1; i <= n; i++)//维护 m 个水龙头的结束时间
    {
        int w;
        cin >> w;
        int t = heap.top();
        heap.pop();//min
        t += w;//累积起来
        heap.push(t);//加入维护
        ret = max(ret, t);
    }
    cout << ret << endl;
    return 0;
}

在这里插入图片描述

目录

  1. 题目清单
  2. 1. 转圈游戏
  3. 2. System Administrator
  4. 3. 多米诺骨牌
  5. 4. 排队接水
  6. 5. 健康的荷斯坦奶牛
  7. 6. 接水问题

更多推荐文章

查看全部
  • Python 数据科学工具链入门:NumPy、Pandas、Matplotlib 实战
  • Odoo 社区版与企业版安装指南(Windows/Linux/Docker)
  • 优雅训服大模型:深入理解与实战 Prompt 技巧
  • 将 Google AI Studio 生成的 Web 应用打包为 Android APK
  • Python 图像处理实战:生成手绘图、证件照与九宫格
  • 如何降低豆包生成论文的 AIGC 检测率?工具实测对比
  • 基于 QQ UI 自动化的 AI 聊天机器人搭建指南
  • Happy Coder:Claude Code 的移动端与 Web 客户端方案
  • 从卡顿到流畅:Tesla K80 显卡上的 llama.cpp CUDA 优化实战指南
  • Spring AI Alibaba 入门与 ReactAgent 使用指南
  • Java 快速开发框架实战对比:若依、芋道、Jeesite、JeecgBoot
  • Web 应用架构与常见安全漏洞学习总结
  • Mac Mini M4 本地部署大模型实战:Ollama 与 Llama 环境搭建
  • 主流 RAG 与 GraphRAG 框架总结及推荐:LightRAG、nano-GraphRAG、Dify
  • New API 详解:开源大模型统一网关与 AI 资产管理系统
  • Python 驱动的 ADS 自动化仿真框架与 API 实战指南
  • PANTONE 潘通色标薄全系列 AI 色板库
  • 上手 RAGFlow:开源 RAG 引擎的部署与实战
  • Claude AI 注册流程解析与手机号验证解决方案
  • Spring Bean 的作用域
  • 相关免费在线工具

    • 加密/解密文本

      使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

    • Gemini 图片去水印

      基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

    • Base64 字符串编码/解码

      将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

    • Base64 文件转换器

      将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

    • Markdown转HTML

      将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

    • HTML转Markdown

      将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online