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位数
- DFS 构造每一位
- 用
dfs(pos, prefix)
表示我们已经确定了前pos-1
位,当前前缀值为prefix
(十进制整数)。 - 若
pos > n
,说明前n
位都已满足条件,检查这个prefix
是否在区间 [a, b] 内,若是则保存。
- 剪枝条件
- 对于第
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 整除」,同样由上式自然保证。
- 数值范围
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;
}