From 8f1e2bd8c2670bffa08f618708b5ed530b3a55be Mon Sep 17 00:00:00 2001 From: Zengtudor Date: Sat, 9 Aug 2025 12:27:50 +0800 Subject: [PATCH] update --- .gitignore | 4 +- src/8/9/809maze.cpp | 116 +++++++++++++++++++++++++++++++++++++++++++ src/8/9/809slope.cpp | 83 +++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/8/9/809maze.cpp create mode 100644 src/8/9/809slope.cpp diff --git a/.gitignore b/.gitignore index 79e6206..2c4c814 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /build /.cache -/test.* \ No newline at end of file +/test.* +*.in +*.ans \ No newline at end of file diff --git a/src/8/9/809maze.cpp b/src/8/9/809maze.cpp new file mode 100644 index 0000000..5839c3a --- /dev/null +++ b/src/8/9/809maze.cpp @@ -0,0 +1,116 @@ +#include +#include +#include + +// OI/ACM风格常用宏定义和快读 +#define FAST_IO std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); +#define FOR(i, start, end) for (int i = (start); i < (end); ++i) + +// 为了代码简洁,使用pair表示坐标 +using pii = std::pair; + +int T, n, m; +std::vector> a; +std::vector pos; // pos[v] = {row, col} 存储稀有度为v的坐标 + +// 检查是否存在一条路径,其开心程度至少为k +// 即,是否存在一条路径,收集了所有稀有度 < k 的宝物 +bool check(int k) { + if (k == 0) { // 开心值为0总是可以达成的(除非n=m=1且a[0][0]=0,但题目定义d=1) + return true; + } + if (k > n * m) { // 不可能达成比n*m还大的开心值 + return false; + } + + std::vector must_visit; + // 收集所有必须访问的点的坐标(稀有度 0, 1, ..., k-1) + for (int i = 0; i < k; ++i) { + must_visit.push_back(pos[i]); + } + + // 检查起点(0,0)是否在必须访问的列表中 + // 如果a[0][0]的稀有度 >= k,则 (0,0) 不在must_visit中,需要手动加入 + if (a[0][0] >= k) { + must_visit.push_back({0, 0}); + } + // 同样,检查终点(n-1,m-1) + if (a[n - 1][m - 1] >= k) { + must_visit.push_back({n - 1, m - 1}); + } + + // 按照坐标排序:行优先,列其次 + // 这是路径在网格中前进的自然顺序 + std::sort(must_visit.begin(), must_visit.end()); + + // 移除因重复添加(0,0)或(n-1,m-1)而产生的重复点 + must_visit.erase(std::unique(must_visit.begin(), must_visit.end()), must_visit.end()); + + + // 检查排序后的点集是否构成一个合法的路径片段序列 + // 即 p_i 的坐标必须不大于 p_{i+1} 的坐标 + for (size_t i = 0; i + 1 < must_visit.size(); ++i) { + pii current = must_visit[i]; + pii next = must_visit[i + 1]; + if (current.first > next.first || current.second > next.second) { + // 如果后一个点的行或列小于前一个点,则无法从 current 到达 next + return false; + } + } + + // 还需要确保路径的起点和终点是(0,0)和(n-1,m-1) + if (must_visit.empty() || must_visit.front() != pii{0, 0} || must_visit.back() != pii{n - 1, m - 1}) { + // 如果排序后,起点不是(0,0)或终点不是(n-1,m-1),则路径不完整 + return false; + } + + return true; +} + + +void solve() { + std::cin >> n >> m; + a.assign(n, std::vector(m)); + pos.assign(n * m, {0, 0}); + + // 读入数据并预处理pos数组 + for (int i = 0; i < n; ++i) { + for (int j = 0; j < m; ++j) { + std::cin >> a[i][j]; + pos[a[i][j]] = {i, j}; + } + } + + // 二分搜索答案 + int low = 0, high = n * m, ans = 0; + while (low <= high) { + int mid = low + (high - low) / 2; + if (check(mid)) { + // 如果check(mid)成功,说明开心程度mid是可以达到的 + // 我们尝试寻求更高的开心程度 + ans = mid; + low = mid + 1; + } else { + // 如果check(mid)失败,说明开心程度mid无法达到 + // 必须降低目标 + high = mid - 1; + } + } + + std::cout << ans << "\n"; +} + +int main() { + // 题目输入为文件,实际比赛中需要取消注释 + // freopen("maze.in", "r", stdin); + // freopen("maze.out", "w", stdout); + + FAST_IO; + + std::cin >> T; + while (T--) { + solve(); + } + + return 0; +} diff --git a/src/8/9/809slope.cpp b/src/8/9/809slope.cpp new file mode 100644 index 0000000..fee2b1e --- /dev/null +++ b/src/8/9/809slope.cpp @@ -0,0 +1,83 @@ +#include +#include +#include +#include + +struct Point { + long long x, y; +}; + +int N, K; +std::vector points; + +// 判定函数:使用 O(N^2) DP 检查是否存在长度至少为 K 的子序列, +// 使得任意两点斜率不小于 C +bool check(double C) { + // 1. 计算每个点的转换值 z[i] = y[i] - C * x[i] + std::vector z(N); + for (int i = 0; i < N; ++i) { + z[i] = static_cast(points[i].y) - C * static_cast(points[i].x); + } + + // 2. O(N^2) DP 求解最长非递减子序列 (LNDS) + std::vector dp(N); + int max_len = 0; // 记录 LNDS 的全局最大长度 + + for (int i = 0; i < N; ++i) { + dp[i] = 1; // 初始化 dp[i] = 1 + for (int j = 0; j < i; ++j) { + if (z[j] <= z[i]) { + dp[i] = std::max(dp[i], dp[j] + 1); + } + } + max_len = std::max(max_len, dp[i]); + } + + // 3. 判断最长长度是否满足要求 + return max_len >= K; +} + +int main() { + // 开启快速 I/O + std::ios_base::sync_with_stdio(false); + std::cin.tie(NULL); + + // --- 【关键修正】 --- + // 从输入中直接读取 N 和 K + std::cin >> N >> K; + + points.resize(N); + for (int i = 0; i < N; ++i) { + std::cin >> points[i].x >> points[i].y; + } + + // 如果 K=1,问题没有意义,选任意一个点都行,斜率可以认为是无穷大。 + // 但题目要求选 "不同" 的两个点,所以 K 至少为 2。 + // 如果 K<2,直接退出或按题意处理。这里我们假设 K>=2。 + if (K < 2) { + // 根据题目具体要求处理,这里简单退出 + return 0; + } + + // 二分答案的范围 + double low = -2e9, high = 2e9; + + // 进行足够多次数的二分以保证精度 + for (int iter = 0; iter < 100; ++iter) { + // --- 【写法优化】 --- + // 使用更稳健的 mid 计算方式 + double mid = low + (high - low) / 2.0; + if (check(mid)) { + // mid 可行,答案可能更大,尝试提高下界 + low = mid; + } else { + // mid 不可行,答案必须更小,降低上界 + high = mid; + } + } + + // 输出结果,设置精度 + std::cout << std::fixed << std::setprecision(10) << low << std::endl; + + return 0; +}