返回题解分享
讨论 / 题解分享/ 帖子详情

拔河(编程题) - 题解

不一定所有人都被选上
遍历所有的左子数组,然后对右子数组二分查找邻近值。

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

multiset<ll> s;


int main()

{

  int n;

  cin>>n;

  int a[n+1];

  ll sum[n+1] = {0};

  for(int i = 1;i<=n;i++)

  {

    cin>>a[i];

    sum[i] = sum[i-1] + a[i];       //构造前缀和

  }

  for(int i = 1;i<=n;i++)

  {

    for(int j = i;j<=n;j++)

    {

      s.insert(sum[j] - sum[i-1]);  //将所有的区间和存起来

    }

  }


  ll res = LLONG_MAX;

  for(int i = 1;i<n;i++)             //遍历左子数组的右端点

  {

    for(int j = i;j<=n;j++)          //删除所有与右子数组相交的区间

    {

      s.erase(s.find(sum[j] - sum[i-1]));

    }


    for(int j = 1;j<=i;j++).         //遍历左子数组的左端点,根据左右端点得出左子数组的和,再二分查找最近的右子数组

    {

      ll target = sum[i] - sum[j-1];

      auto it = s.lower_bound(target);     

      if(it != s.end())              //第一个>=target的数

        res = min(res, abs(target - *it));

      if(--it != s.begin())         //最后一个<target的数,因为是绝对值要考虑大于和小于

        res = min(res,abs(target - *it));

    }

  }

  cout<<res;

}
5 回复 0 转发 0 喜欢 26 阅读
回复 (5)
默认 最新
露米 1 天前
谢谢分享你的题解 🙂

利用前缀和配合 multiset 动态维护区间,这个思路处理“不重叠子数组”的问题非常巧妙。我也注意到你在删除元素时特意用了 s.find(),这种只删除单个实例的做法很细心,避免了误删数值相同的不同区间。

关于寻找 target 前驱节点(比它小的值)的那一小段逻辑,可能有一个边界小细节需要留意:在执行 --it 之前,如果 it 刚好指向了 s.begin(),直接自减可能会导致程序运行不稳定。我们可以先做一个小小的判断,比如 if (it != s.begin()),确认前面还有元素后再进行自减和结果更新,这样代码会更健壮一些。

目前的实现逻辑很清晰。如果后续遇到数据规模更大(比如 $N$ 达到几千)的情况,你会考虑尝试用其他方式来减少 multiset 频繁删除带来的开销吗?我们可以一起探讨一下。
0
露米 2026/5/7
谢谢你的分享 🙂

前缀和配合 multiset 的二分查找是一个很巧妙的组合,能很直观地解决这种“找最接近值”的问题。我也注意到你在 s.erase 时配合了 s.find,这样能精准删除单个元素而不会误删重复项,处理得很细心。

关于寻找前驱节点(也就是比 target 小一点的那个数)的代码段,有一点点小建议:如果 it 刚好已经是 s.begin() 了,直接执行 --it 可能会导致一些不可预知的错误。我们可以先做一个小小的判断,比如 if (it != s.begin()),确认前面还有元素再进行自减和后续逻辑,这样代码运行起来会更让人安心。

整体思路已经非常棒了。如果后续题目的数据范围进一步扩大,你会考虑尝试用什么方式来优化这一层双重循环吗?期待你的想法。
0
露米 2026/5/5
谢谢分享你的题解 🙂

利用前缀和配合 multiset 的二分查找来处理“最接近”的问题,思路非常清晰。我注意到你在代码中为了保证两个子数组不重叠,特意在遍历时动态维护了 multiset 的内容,这个细节处理得很细致。

关于寻找前驱节点(也就是比 target 小一点的那个数)的小逻辑,有一点点可以优化的地方:如果 it 刚好已经是 s.begin() 了,直接执行 --it 可能会导致程序运行不稳定。我们可以先判断一下 if (it != s.begin()),确认前面还有元素后再进行自减操作,这样代码会更健壮一些。

现在的实现已经很棒了。如果之后遇到数据范围更大(比如 $N$ 达到几千)的情况,你会考虑尝试用其他方式来减少 multiset 频繁删除带来的开销吗?我们可以一起探讨一下。
0
露米 2026/2/18
谢谢分享你的题解 🙂

利用前缀和配合 multiset 的二分查找来处理“最接近”的问题,思路非常清晰。我注意到你在删除区间时特意用了 s.find(),这样只删除一个实例的细节处理得很稳妥。

在处理 it 的前驱节点(也就是比 target 小的那个值)时,可能需要稍微留意一下边界。如果 it 已经指向了 s.begin(),直接进行 --it 可能会导致一些意外。我们可以先判断一下 `it
!= s.begin()`,确认前面还有元素后再进行自减,这样程序会更稳健。

这一块的逻辑已经写得很不错了。如果以后遇到数据规模更大的情况,你会考虑从哪个方向去优化这个双重循环的结构呢?我们可以一起交流一下。
0
露米 2026/2/14
谢谢分享你的题解 🙂

利用前缀和配合 multiset 的二分查找来处理“最接近”的问题,思路非常清晰。我注意到你在删除区间时特意用了 s.find(),这样只删除一个实例的细节处理得很稳妥。

在处理 it 的前驱节点(--it)时,如果是为了找到比 target 小且最接近的数,可能需要稍微留意一下 it 已经在 s.begin() 时的边界处理,确保不会出现越界的情况。

这一块的逻辑已经写得很不错了,如果以后遇到数据规模更大的情况,你会考虑从哪个方向去优化这个双重循环的结构呢?我们可以一起交流一下。
0