题解:洛谷 P10377 好斗的牛

【题目来源】

洛谷:P10377 [GESP202403 六级] 好斗的牛 - 洛谷

【题目描述】

你有 \(10^9\) 个牛棚,从左到右一字排开。你希望把 \(n\) 头牛安置到牛棚里。麻烦的是,你的牛很好斗,如果他们附近有其他的牛,他们就会不安分地去挑事。其中,第 \(i\) 头牛的攻击范围是 \((a_i, b_i)\),这意味着,如果他的左边 \(a_i\) 个牛棚或者右边 \(b_i\) 个牛棚有其他牛,它就会去挑事。

你想留下一段连续的牛棚,并把其他牛棚都卖掉。请问您最少需要留下多少牛棚,才能保证至少存在一种方案能够把所有的 \(n\) 头牛都安置进剩余的牛棚里,且没有牛会挑事?

【输入】

第一行一个正整数 \(n\)
第二行 \(n\) 个正整数 \(a_1, a_2, \dots a_n\)
第三行 \(n\) 个正整数 \(b_1, b_2, \dots b_n\)

【输出】

输出一行一个整数表示答案。

【输入样例】

2
1 2
1 2

【输出样例】

4

【核心思想】

  1. 问题分析:有 \(n\) 头牛,每头牛有攻击范围 \((a_i, b_i)\),即左边 \(a_i\) 个牛棚和右边 \(b_i\) 个牛棚内不能有其他牛。需要找到一种牛的排列顺序和间距,使得所有牛安置所需的最少连续牛棚数。这是一个全排列枚举 + 贪心间距计算问题,核心在于枚举所有牛的排列顺序,计算每种排列所需的最小总距离。

  2. 算法选择

    • DFS 全排列:枚举 \(n\) 头牛的所有排列顺序(\(n!\) 种)
    • 贪心间距计算:对于给定排列,相邻两头牛 \(i\)(左)和 \(j\)(右)之间的最小安全距离为 \(\max(a_j, b_i) + 1\)(间隔 \(\max(a_j, b_i)\) 个空牛棚,再加 \(1\) 个牛棚放牛 \(j\)
  3. 关键步骤

    • 读入数据:读取 \(n\) 和数组 \(a[1..n], b[1..n]\)
    • DFS 生成全排列\(dfs(step)\)):
      • \(step > n\):计算当前排列的总距离
        • \(tot \leftarrow 1\)(第一头牛占 \(1\) 个牛棚)
        • 遍历 \(i\)\(2\)\(n\)
          • \(tot \leftarrow tot + \max(cow[a[i]].a, cow[a[i-1]].b) + 1\)
        • \(minn \leftarrow \min(minn, tot)\)
      • 否则:枚举未使用的牛,标记后递归,回溯
    • 输出结果\(minn\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n! \cdot n)\),全排列枚举 \(n!\) 种,每种计算距离 \(O(n)\)
    • 空间复杂度:\(O(n)\),递归栈和标记数组
  5. 全排列枚举与贪心间距的核心思想

    • 相邻牛的安全距离:对于排列中相邻的牛 \(p\)(左)和 \(q\)(右),牛 \(q\) 的左边需要 \(a_q\) 个空牛棚,牛 \(p\) 的右边需要 \(b_p\) 个空牛棚。两者共享中间区域,因此最小间隔为 \(\max(a_q, b_p)\) 个空牛棚,加上牛 \(q\) 自身占的 \(1\) 个牛棚
    • 全排列的必要性:牛的排列顺序直接影响总距离,不同顺序下相邻关系不同,需要枚举所有可能找到最优解
    • DFS 回溯:通过标记数组 \(flag\) 实现排列生成,递归后回溯取消标记,确保每种排列只被考虑一次
    • 第一头牛的特殊处理:第一头牛没有左边的牛,只需 \(1\) 个牛棚。后续每头牛需要 \(\max(a_{current}, b_{previous}) + 1\) 个牛棚(间隔 + 自身位置)
    • 适用于小规模排列优化、相邻约束类 DFS 枚举问题

【算法标签】

普及- #DFS-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int n;               // 牛的数量
int flag[15];        // 标记数组,用于记录牛是否被使用
int a[15];           // 排列数组,存储当前排列的牛的顺序
int minn = 1e9;      // 最小总距离,初始化为一个大数

struct Node
{
    int a;  // 牛的左边距离
    int b;  // 牛的右边距离
}cow[15];   // 牛的信息数组

// 深度优先搜索生成全排列
void dfs(int step)
{
    // 如果已经排列完所有牛,计算当前排列的总距离
    if (step > n)
    {
        int tot = 1;  // 初始化总距离,第一头牛需要一个牛棚
        for (int i = 2; i <= n; i++)  // 从第二头牛开始计算
        {
            // 计算相邻两头牛之间的栅栏距离
            // 取左边牛的右边距离和右边牛的左边距离的最大值
            tot += max(cow[a[i]].a, cow[a[i-1]].b);
            tot += 1;  // 当前这头牛也需要一个牛棚
        }
        // 更新最小总距离
        minn = min(minn, tot);
        return;
    }

    // 生成全排列
    for (int i = 1; i <= n; i++)  // 尝试将每头牛放在当前位置
    {
        if (!flag[i])  // 如果这头牛还没有被使用
        {
            flag[i] = 1;     // 标记为已使用
            a[step] = i;     // 将牛i放在第step个位置
            dfs(step + 1);    // 递归放置下一头牛
            flag[i] = 0;     // 回溯,取消标记
        }
    }
}

int main()
{
    // 输入牛的数量
    cin >> n;

    // 输入每头牛左边的距离
    for (int i = 1; i <= n; i++)
    {
        cin >> cow[i].a;
    }

    // 输入每头牛右边的距离
    for (int i = 1; i <= n; i++)
    {
        cin >> cow[i].b;
    }

    // 从第一个位置开始深度优先搜索
    dfs(1);

    // 输出最小总距离
    cout << minn << endl;

    return 0;
}

【运行结果】

2
1 2
1 2
4
posted @ 2026-07-17 15:47  团爸讲算法  阅读(6)  评论(0)    收藏  举报