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

拔河(编程题) - 题解

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

#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;

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

利用前缀和配合 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