当前位置:首页 > 技术 > 正文内容

二分查找与深度优先搜索算法应用解析

访客 技术 2026年7月24日 1

二分查找边界定位实现

在有序序列中定位目标值的首次出现位置,需处理边界条件。数组容量不足是常见错误,需根据数据规模合理分配内存。关键逻辑在于当中间值等于目标时,继续向左边界搜索以定位首次出现位置。

#include <iostream>
#include <climits>
using namespace std;
const int MAXN = 1000007;
int data[MAXN];
int binary_first(int target, int size) {
    int left = 0, right = size + 1;
    data[0] = INT_MIN;
    data[size + 1] = INT_MAX;
    while (left + 1 < right) {
        int mid = (left + right) / 2;
        if (data[mid] >= target) right = mid;
        else left = mid;
    }
    return data[right] == target ? right : -1;
}
int main() {
    int n, q, val;
    cin >> n >> q;
    for (int i = 1; i <= n; i++) cin >> data[i];
    while (q--) {
        cin >> val;
        cout << binary_first(val, n) << ' ';
    }
    return 0;
}

深度优先搜索实现全排列

经典排列问题通过递归回溯实现。使用访问标记数组防止元素重复使用,当路径长度达到元素总数时输出当前排列。注意输出格式要求固定宽度对齐。

#include <iomanip>
#include <vector>
using namespace std;
vector<int> path;
vector<bool> visited;
void dfs_permute(int current, int total) {
    if (current > total) {
        for (int num : path) cout << setw(5) << num;
        cout << '\n';
        return;
    }
    for (int i = 1; i <= total; i++) {
        if (!visited[i]) {
            visited[i] = true;
            path.push_back(i);
            dfs_permute(current + 1, total);
            path.pop_back();
            visited[i] = false;
        }
    }
}
int main() {
    int n;
    cin >> n;
    visited.resize(n + 1, false);
    dfs_permute(1, n);
    return 0;
}

巧克力分割的二分求解

通过二分法确定最大切割边长。验证函数计算给定尺寸可分割的巧克力总数,需注意面积计算应为长宽可分割数量的乘积而非加和。切割尺寸与可分割数量呈反比关系。

#include <algorithm>
using namespace std;
bool validate(int size, int rows[], int cols[], int count, int need) {
    long total = 0;
    for (int i = 0; i < count; i++) {
        total += static_cast<long>(rows[i]/size) * (cols[i]/size);
        if (total >= need) return true;
    }
    return false;
}
int main() {
    int n, k, max_dim = 0;
    cin >> n >> k;
    int heights[n], widths[n];
    for (int i = 0; i < n; i++) {
        cin >> heights[i] >> widths[i];
        max_dim = max(max_dim, max(heights[i], widths[i]));
    }
    int low = 0, high = max_dim + 1;
    while (low + 1 < high) {
        int mid = (low + high) / 2;
        validate(mid, heights, widths, n, k) ? low = mid : high = mid;
    }
    cout << low << endl;
    return 0;
}

木材切割的二分应用

典型二分答案问题。切割长度与可获得段数成反比,验证函数计算当前长度下可获得的木材段数。特别处理输入为零的边界情况。

bool check_cut(int len, int logs[], int count, int need) {
    if (len == 0) return false;
    long segments = 0;
    for (int i = 0; i < count; i++) {
        segments += logs[i] / len;
        if (segments >= need) return true;
    }
    return false;
}
int main() {
    int n, k;
    cin >> n >> k;
    int timber[n], max_len = 0;
    for (int i = 0; i < n; i++) {
        cin >> timber[i];
        max_len = max(max_len, timber[i]);
    }
    int left = 0, right = max_len + 1;
    while (left + 1 < right) {
        int mid = (left + right) / 2;
        check_cut(mid, timber, n, k) ? left = mid : right = mid;
    }
    cout << left << endl;
    return 0;
}

猫粮规划的剪枝优化

组合问题使用DFS遍历所有可能子集。通过排序预处理和当前和判断实现剪枝:当累计值超过上限时终止当前分支搜索,显著提升效率。

#include <algorithm>
using namespace std;
int count_subsets(int nums[], int idx, int total, int n, int low, int high) {
    int valid = (total >= low && total <= high) ? 1 : 0;
    if (total > high || idx >= n) return valid;
    for (int i = idx; i < n; i++) 
        valid += count_subsets(nums, i + 1, total + nums[i], n, low, high);
    return valid;
}
int main() {
    int n, low_bound, up_bound;
    cin >> n >> low_bound >> up_bound;
    int portions[n];
    for (int i = 0; i < n; i++) cin >> portions[i];
    sort(portions, portions + n);
    cout << count_subsets(portions, 0, 0, n, low_bound, up_bound) << endl;
    return 0;
}

K皇后攻击范围标记法

使用四个标记数组分别处理行、列、主副对角线。避免二维数组遍历,通过数学关系直接计算对角线标识。被标记位置即为可攻击区域。

int main() {
    int rows, cols, queens;
    cin >> rows >> cols >> queens;
    bool row_mark[rows+1] = {0}, col_mark[cols+1] = {0};
    bool diag1[rows+cols+1] = {0}, diag2[rows+cols+1] = {0};
    while (queens--) {
        int r, c;
        cin >> r >> c;
        row_mark[r] = col_mark[c] = true;
        diag1[r + c] = diag2[rows + r - c] = true;
    }
    int safe_count = 0;
    for (int r = 1; r <= rows; r++) {
        if (row_mark[r]) continue;
        for (int c = 1; c <= cols; c++) {
            if (!col_mark[c] && !diag1[r+c] && !diag2[rows+r-c]) 
                safe_count++;
        }
    }
    cout << safe_count;
    return 0;
}

路标设置的二分答案

空旷指数与需添加路标数量成反比。验证函数计算当前空旷指数下需要补充的路标总数,通过相邻路标间距计算需插入的数量。采用左开右闭二分搜索确定最小值。

bool check_gaps(int gap, int markers[], int count, int total_len, int max_add) {
    long need = 0;
    need += (markers[0] - 1) / gap;
    for (int i = 1; i < count; i++) 
        need += (markers[i] - markers[i-1] - 1) / gap;
    need += (total_len - markers[count-1] - 1) / gap;
    return need <= max_add;
}
int main() {
    int length, existing, additions;
    cin >> length >> existing >> additions;
    int positions[existing];
    for (int i = 0; i < existing; i++) cin >> positions[i];
    int low = 0, high = length;
    while (low + 1 < high) {
        int mid = (low + high) / 2;
        check_gaps(mid, positions, existing, length, additions) ? high = mid : low = mid;
    }
    cout << high;
    return 0;
}

设备供电的浮点二分

设备运行时间与所需充电量成正比。验证函数计算总运行时间下充电宝需补充的能量总和,注意浮点数精度处理。特判无限运行的情况。

#include <cmath>
const double EPS = 1e-5;
bool check_runtime(double time, double consume[], double battery[], int n, double power) {
    double required = 0;
    for (int i = 0; i < n; i++) {
        double need = consume[i] * time;
        if (need > battery[i]) required += need - battery[i];
    }
    return required <= power * time;
}
int main() {
    int devices;
    double power;
    cin >> devices >> power;
    double consume[devices], battery[devices];
    for (int i = 0; i < devices; i++) cin >> consume[i] >> battery[i];
    double low = 0, high = 1e10;
    while (high - low > EPS) {
        double mid = (low + high) / 2;
        check_runtime(mid, consume, battery, devices, power) ? low = mid : high = mid;
    }
    fabs(high - 1e10) < EPS ? cout << "-1" : cout << low;
    return 0;
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。