题解:洛谷 B3930 烹饪问题

【题目来源】

洛谷:B3930 [GESP202312 五级] 烹饪问题 - 洛谷

【题目描述】

\(N\) 种食材,编号从 \(1\)\(N\),其中第 \(i\) 种食材的美味度为 \(a_i\)

不同食材之间的组合可能产生奇妙的化学反应。具体来说,如果两种食材的美味度分别为 \(x\)\(y\) ,那么它们的契合度为 $x\ \text{and}\ y $。

其中,\(\text{and}\) 运算为按位与运算,需要先将两个运算数转换为二进制,然后在高位补足,再逐位进行与运算。例如,\(12\)\(6\) 的二进制表示分别为 \(1100\)\(0110\) ,将它们逐位进行与运算,得到 \(0100\) ,转换为十进制得到 4,因此 \(12\ \text{and}\ 6 = 4\)在 C++ 或 Python 中,可以直接使用 & 运算符表示与运算。

现在,请你找到契合度最高的两种食材,并输出它们的契合度。

【输入】

第一行一个整数 \(N\),表示食材的种数。

接下来一行 \(N\) 个用空格隔开的整数,依次为 \(a_1,\cdots,a_N\),表示各种食材的美味度。

【输出】

输出一行一个整数,表示最高的契合度。

【输入样例】

3
1 2 3

【输出样例】

2

【核心思想】

  1. 问题分析:给定 \(N\) 个整数 \(a_1, a_2, \ldots, a_N\),求 \(\max_{i \neq j}(a_i \ \& \ a_j)\),即两两按位与的最大值。这是一个按位贪心 + 排序剪枝问题,关键在于利用按位与的性质:若 \(a_i \ \& \ a_j = v\),则 \(a_i \geq v\)\(a_j \geq v\)

  2. 算法选择

    • 降序排序 + 剪枝:将数组从大到小排序,外层枚举 \(a_i\),若 \(a_i \leq\) 当前最大值则剪枝(因为后续元素更小,与运算结果不可能更大)
    • 内层枚举:与 \(a_i\) 后面的元素进行与运算,更新最大值
  3. 关键步骤

    • 读入数据\(N\) 和数组 \(a[1..N]\)
    • 降序排序sort(a+1, a+n+1, greater<int>())
    • 优化枚举\(i\)\(1\)\(N\)):
      • 剪枝:若 a[i] <= maxncontinue(后续元素更小,无法产生更大与值)
      • 内层 \(j\)\(i+1\)\(N\)maxn = max(maxn, a[i] & a[j])
    • 输出 maxn
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N + N^2)\),排序 \(O(N \log N)\),最坏情况仍 \(O(N^2)\),但剪枝大幅减少实际运算
    • 空间复杂度:\(O(N)\),存储数组
  5. 按位与贪心剪枝的核心思想

    • 与运算的单调性\(a_i \ \& \ a_j \leq \min(a_i, a_j)\)。因此若 \(a_i \leq\) 当前最大与值,则 \(a_i\) 与任何数的与值 \(\leq a_i \leq\) 当前最大值,无需继续枚举
    • 降序排序的优势:大数在前,更容易快速找到较大的与值,使 maxn 尽早变大,从而触发更多剪枝
    • 高位优先效应:按位与的结果高位为1比低位为1更重要,降序排序使高位为1的数优先配对
    • 适用于"最大按位与/或"问题,核心在于利用与运算的上界性质进行剪枝优化

【算法标签】

普及 #贪心

【代码详解】

// 40分版本
#include <bits/stdc++.h>
using namespace std;

const int N = 1000005;  // 最大数组长度
int n;                  // 数组元素个数
int maxn = -1e9;        // 最大与运算结果,初始化为极小值
int a[N];               // 存储输入数组

int main()
{
    // 输入数组大小
    cin >> n;

    // 输入数组元素
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    // 暴力枚举所有不相同的元素对
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            // 跳过相同的元素对
            if (i == j)
            {
                continue;
            }

            // 计算按位与并更新最大值
            maxn = max(maxn, a[i] & a[j]);
        }
    }

    // 输出最大的按位与结果
    cout << maxn << endl;

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

const int N = 1000005;  // 最大数组长度
int n;                  // 数组元素个数
int maxn = -1e9;        // 最大与运算结果,初始化为极小值
int a[N];               // 存储输入数组

int main()
{
    // 输入数组大小
    cin >> n;

    // 输入数组元素
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    // 将数组从大到小排序
    sort(a + 1, a + n + 1, greater<int>());

    // 优化后的双重循环
    for (int i = 1; i <= n; i++)
    {
        // 剪枝:如果当前元素已经小于等于当前最大值,跳过
        if (a[i] <= maxn)
        {
            continue;
        }

        // 与后面的元素进行按位与运算
        for (int j = i + 1; j <= n; j++)
        {
            maxn = max(maxn, a[i] & a[j]);
        }
    }

    // 输出最大的按位与结果
    cout << maxn << endl;

    return 0;
}

【运行结果】

3
1 2 3
2
posted @ 2026-08-10 12:24  团爸讲算法  阅读(15)  评论(0)    收藏  举报