2025年天梯题解(L1-8 + L2)

L1-112 现代战争

题目

既然是从大到小轰炸,将所有点存储为三元组(value, x, y)

排序之后, 记录行列被轰炸的编号,进行 k 次挑选即可。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

constexpr int MAXN = 1000;
struct Node { int val, r, c; };
static Node v[MAXN*MAXN + 5];
static int  mat[MAXN+5][MAXN+5];
static bool row_boom[MAXN+5], col_boom[MAXN+5];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m, k;
    cin >> n >> m >> k;

    // 1) 读入矩阵,扁平化到 v[]
    int tot = 0;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= m; j++){
            cin >> mat[i][j];
            v[tot++] = { mat[i][j], i, j };
        }
    }

    // 2) 按值从大到小排序
    sort(v, v + tot, [](auto &A, auto &B){
        return A.val > B.val;
    });

    // 3) 选 k 个"炸弹"行列
    int cnt = 0;
    for(int i = 0; i < tot && cnt < k; i++){
        int rr = v[i].r, cc = v[i].c;
        if(!row_boom[rr] && !col_boom[cc]){
            row_boom[rr] = true;
            col_boom[cc] = true;
            cnt++;
        }
    }

    // 4) 输出剩余元素
    for(int i = 1; i <= n; i++){
        if(row_boom[i]) continue;
        bool first = true;
        for(int j = 1; j <= m; j++){
            if(col_boom[j]) continue;
            if(!first) cout << ' ';
            cout << mat[i][j];
            first = false;
        }
        if(!first) cout << '\n';
    }
    return 0;
}

L2-051 满树的遍历

题目

先将输入的全括号算式解析成一棵二叉树,然后做后序遍历------即先处理左子树、再处理右子树、最后输出当前节点的操作。输出时,如果左右子节点是叶子数字,就打印该数字;否则不打印。这样完全符合题目要"只输出数字+操作符"的格式要求。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

struct Node {
    // 如果 isLeaf,则 val 存数字字符串;否则存操作符 op,以及左右子节点指针
    bool isLeaf;
    string val;
    char op;
    Node *l, *r;
    Node(string v): isLeaf(true), val(move(v)), op(), l(nullptr), r(nullptr) {}
    Node(char c, Node* L, Node* R): isLeaf(false), val(), op(c), l(L), r(R) {}
};

string s;
int pos;

// 解析函数:根据语法 expr = '(' expr op expr ')' | number
Node* parse() {
    if (s[pos] == '(') {
        ++pos; // skip '('
        Node* L = parse();
        char oper = s[pos++];
        Node* R = parse();
        ++pos; // skip ')'
        return new Node(oper, L, R);
    } else {
        // 读数字
        int start = pos;
        while (pos < (int)s.size() && isdigit(s[pos])) ++pos;
        return new Node(s.substr(start, pos - start));
    }
}

// 后序遍历,按题目要求输出每一步
void dfs(Node* cur) {
    if (cur->isLeaf) return;
    dfs(cur->l);
    dfs(cur->r);
    // 下面输出:如果左是叶子,先打印数字;然后打印操作符;如果右是叶子,再打印数字
    if (cur->l->isLeaf) cout << cur->l->val;
    cout << cur->op;
    if (cur->r->isLeaf) cout << cur->r->val;
    cout << "\n";
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    // 读入整行
    if (!getline(cin, s)) return 0;
    pos = 0;
    Node* root = parse();
    dfs(root);
    return 0;
}

L2-054 三点共线

题目

可以使用 bitset 优化枚举过这道题。

功能 bitset unordered_set<int>
插入/查询速度 O(1) (硬件位操作) O(1) 均摊(有哈希冲突)
空间占用 1 bit/值(非常省) 至少 8 字节/值 + 哈希桶
是否能负数索引 ❌ 需要 偏移量映射 ✅ 不限制值域
  • bitset 的基本用法
cpp 复制代码
#include <bitset>

bitset<10> b;      // 创建一个包含10位的bitset,默认全是0
b[3] = 1;          // 将第4位设为1(下标从0开始)
b.set(5);          // 将第6位设为1
b.reset(3);        // 将第4位设为0
b.flip();          // 所有位取反
bool x = b.test(5); // 判断第6位是否为1(返回true/false)
int cnt = b.count(); // 返回有多少位是1
  • Code
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int D = 1e6;  // 坐标偏移量
bitset<2000010> pos; // 存储y=2的点

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    vector<vector<int>> layers(2);  // layers[0]:y=0, layers[1]:y=1
    int n, xpmclzjkln = 0;  // 题目要求的中间变量
    
    cin >> n;
    for (int i = 0; i < n; ++i) {
        int x, y;
        cin >> x >> y;
        if (y == 2) {
            pos.set(x + D);  // 坐标偏移防止负数
        } else {
            layers[y].push_back(x);
        }
    }

    // 去重排序
    for (auto& layer : layers) {
        sort(layer.begin(), layer.end());
        layer.erase(unique(layer.begin(), layer.end()), layer.end());
    }

    vector<pair<int, int>> ans;
    for (int x0 : layers[0]) {
        for (int x1 : layers[1]) {
            int x2 = 2 * x1 - x0;
            if(x2 < -1E6 || x2 > 1E6) continue;
            if (pos.test(x2 + D)) {
                ans.emplace_back(x0, x1);
            }
        }
    }

    // 按要求排序
    sort(ans.begin(), ans.end(), [](auto& a, auto& b) {
        return tie(a.second, a.first) < tie(b.second, b.first);
    });

    // 输出结果
    if (ans.empty()) {
        cout << -1;
    } else {
        for (auto& [x0, x1] : ans) {
            printf("[%d, 0] [%d, 1] [%d, 2]\n", 
                  x0, x1, 2*x1 - x0);
        }
    }
    return 0;
}

L2-055 胖达的山头

差分取 max

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int parse_time(const string &s) {
    // "hh:mm:ss" -> 秒数
    int hh = stoi(s.substr(0,2));
    int mm = stoi(s.substr(3,2));
    int ss = stoi(s.substr(6,2));
    return hh*3600 + mm*60 + ss;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    vector<pair<int,int>> events;
    events.reserve(2*n);

    string s_start, s_end;
    for(int i = 0; i < n; i++){
        cin >> s_start >> s_end;
        int st = parse_time(s_start);
        int ed = parse_time(s_end);
        // [st, ed] 包含端点,故在 ed+1 时做 −1
        events.emplace_back(st, +1);
        events.emplace_back(ed+1, -1);
    }

    // 按时间排序
    sort(events.begin(), events.end(),
         [](auto &a, auto &b){
             if(a.first != b.first) 
                 return a.first < b.first;
             return a.second < b.second; // +1 也可以先后都累加
         });

    int cur = 0, ans = 0;
    for(auto &e : events){
        cur += e.second;
        ans = max(ans, cur);
    }

    cout << ans << "\n";
    return 0;
}

L2-056 被n整除的n位数

题目

  1. DFS 构造每一位
  • dfs(pos, prefix) 表示我们已经确定了前 pos-1 位,当前前缀值为 prefix(十进制整数)。
  • pos &gt; n,说明前 n 位都已满足条件,检查这个 prefix 是否在区间 [a, b] 内,若是则保存。
  1. 剪枝条件
  • 对于第 pos 位(1-based,下同),我们尝试的数字 d 必须使得:

( p r e f i x × 10 + d )   m o d   p o s = 0. (prefix \times 10 + d) \bmod pos = 0. (prefix×10+d)modpos=0.

  • 另外,第 1 位 pos=1 只能枚举 1..9(最高位不能为 0);后续可枚举 0..9,但按照提示:若 pos=n,即剩下最后一位,还要满足「能被 n 整除」,同样由上式自然保证。
  1. 数值范围
  • n <= 14 时,深度 DFS 的分支被整除条件严格限制,一般仅产生几十到几百个候选,远小于暴力 1 0 n 10^n 10n。
  • long long 存前缀、比较区间,安全无溢出。
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

int n;
ll A, B;
vector<ll> answer;

// DFS 生成
void dfs(int pos, ll prefix) {
    if (pos > n) {
        // 构造了 n 位数,检查区间
        if (prefix >= A && prefix <= B) {
            answer.push_back(prefix);
        }
        return;
    }
    int start = (pos == 1 ? 1 : 0);
    for (int d = start; d <= 9; d++) {
        ll nxt = prefix * 10 + d;
        // 前 pos 位能被 pos 整除
        if (nxt % pos == 0) {
            dfs(pos + 1, nxt);
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> A >> B;
    answer.clear();

    // n 位数的最小与最大
    ll low = 1;
    for (int i = 1; i < n; i++) low *= 10;
    ll high = low * 10 - 1;
    // 如果 [A,B] 与 n 位数区间无交集,直接无解
    if (B < low || A > high) {
        cout << "No Solution\n";
        return 0;
    }
    // 交集后再 DFS,会节省一点无谓判断
    A = max(A, low);
    B = min(B, high);

    dfs(1, 0LL);

    if (answer.empty()) {
        cout << "No Solution\n";
    } else {
        sort(answer.begin(), answer.end());
        for (ll x : answer) {
            cout << x << "\n";
        }
    }
    return 0;
}
相关推荐
__lost38 分钟前
MATLAB退火算法和遗传算法解决旅行商问题
开发语言·算法·matlab·遗传算法·退火算法
恶霸不委屈1 小时前
MATLAB函数调用全解析:从入门到精通
开发语言·算法·matlab·匿名函数·函数句柄
dog2501 小时前
BBR 的 RTT 公平性问题求解
人工智能·算法·机器学习
不搞学术柒柒2 小时前
《分词算法大揭秘:BPE、BBPE、WordPiece、ULM常见方法介绍》
算法
安装虚拟机的老师傅3 小时前
当插入排序遇上“凌波微步“——希尔排序的奇幻漂流
数据结构·算法·排序算法
大魔王(已黑化)3 小时前
LeetCode —— 572. 另一棵树的子树
c语言·数据结构·c++·算法·leetcode·职场和发展
机器学习之心HML3 小时前
JCRQ1河马算法+消融实验!HO-CNN-LSTM-Attention系列四模型多变量时序预测,作者:机器学习之心
算法·机器学习·cnn
是店小二呀4 小时前
【优选算法 | 前缀和】前缀和算法:高效解决区间求和问题的关键
android·c++·算法
Cuit小唐4 小时前
C++好用的打印日志类
开发语言·c++·算法
福理原乡大王4 小时前
Linux环境变量
linux·运维·服务器·数据结构·算法