模型思路如下:
/*
* @Author: YMYS
* @Date: 2024-12-25 20:43:41
* @LastEditTime: 2024-12-26 09:55:06
* @FilePath: \VScode-C&C++-Coding\Acwing\蓝桥杯辅导课\C++b组-往届省赛真题\15\5990.拔河.cpp
* @URL:https://www.acwing.com/problem/content/5993/
* @Description: 5990. 拔河
*
* 这道题提到了区间和,就一定会用到前缀和。
*
* 难点就在于建立一个什么样的模型去遍历整个数组找最小差距。
* 我们使用通用的遍历模型即可,即:最外层循环遍历[1,n],将整个完整区间切分成左和右两个区间
* 然后内层第一个循环不断的存入左遍子区间的和,内层第二个循环不断的计算右边子区间的sum,然后在multiset数组中找地址即可。
*
*
*/
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1010;
int n;
int s[N];
multiset<int> ranges;
signed main()
{
#ifdef ABC
freopen("D:\\daily_Coding\\VScode-C&C++-Coding\\in.in", "r", stdin);
freopen("D:\\daily_Coding\\VScode-C&C++-Coding\\out.out", "w", stdout);
#endif
cin>>n;
//读取数据,并计算出前缀和数组
for (int i = 1; i <= n; i ++ )
{
int x;
cin>>x;
//前缀和数组:s[1]=x1、s[2]=x1+x2、s[3]=x1+x2+x3、 ...、s[n]=x1+x2+...+xn
s[i] = s[i - 1] + x;
}
//初始化multiset【就是存入最大边界和最小边界】
ranges.insert(1e15), ranges.insert(-1e15);
int res = 1e9;//初始化答案为极大值
//我们让一根 “中间指针” 遍历整个数组,
//该“指针”将整个数组分为两段,对两段再分别求各个小子区间的和
for (int l = 1; l <= n; l ++ )
{
//一、先求和【这里求的是左区间的值】
//下面这个循环执行完以后,ranges数组的值为:[1,l-1]、[2,l-1]、[3,l-1]、[4,l-1] ... [i,l-1]
//并且ranges数组会自动按升序排好序,且不去重
for (int i = 1; i <= l - 1; i ++ )
ranges.insert(s[l - 1] - s[i - 1]); // 增加区间[i, l - 1]
//二、再找值【这里找的是右区间的值】
//不断遍历右区间,记录sum的值,存min的左右两区间差距
//因为ranges本来就是从小到大排列的,所以左边区间的值一定比右边区间的值要小
//既然我们想遍历求出左右区间差值最小,那就分别取右边区间最小值和左边区间最大值
for (int r = l; r <= n; r ++ )
{
int sum = s[r] - s[l - 1];//[l, r]的区间和
auto it = ranges.lower_bound(sum);//在区间和里找到第一个大于等于sum的值,返回首地址
res = min(res, *it - sum);//【*it - sum】:这里求的是右目标区间的最小值的最小差距
-- it;
res = min(res, sum - *it);//【sum - *it】:这里求的是左目标区间的最大值的最小差距
}
}
cout<<res<<endl;
return 0;
}