P10429 [蓝桥杯 2024 省 B] 拔河
题目描述
小明是学校里的一名老师,他带的班级共有 nnn 名同学,第 iii 名同学力量值为 aia_iai。在闲暇之余,小明决定在班级里组织一场拔河比赛。
为了保证比赛的双方实力尽可能相近,需要在这 nnn 名同学中挑选出两个队伍,队伍内的同学编号连续 {al1,al1+1,…,ar1−1,ar1}\{{a_{l_1}}, a_{l_1 + 1}, \dots, a_{r_1 - 1}, a_{r_1}\}{al1,al1+1,…,ar1−1,ar1} 和 {al2,al2+1,…,ar2−1,ar2}\{{a_{l_2}}, a_{l_2 + 1}, \dots, a_{r_2 - 1}, a_{r_2}\}{al2,al2+1,…,ar2−1,ar2},其中 l1≤r1<l2≤r2l_1 \le r_1<l_2 \le r_2l1≤r1<l2≤r2。
两个队伍的人数不必相同,但是需要让队伍内的同学们的力量值之和尽可能相近。请计算出力量值之和差距最小的挑选队伍的方式。
输入格式
输入共两行。
第一行为一个正整数 nnn。
第二行为 nnn 个正整数 a1,a2,…ana_1, a_2, \dots a_na1,a2,…an。
输出格式
输出共一行,一个非负整数,表示两个队伍力量值之和的最小差距。
输入输出样例 #1
输入 #1
5
10 9 8 12 14
输出 #1
1
说明/提示
样例 1 解释
其中一种最优选择方式:
队伍 1:{a1,a2,a3}\{a_1, a_2, a_3\}{a1,a2,a3},队伍 2:{a4,a5}\{a_4, a_5\}{a4,a5},力量值和分别为 10+9+8=2710 + 9 + 8 = 2710+9+8=27,12+14=2612 + 14 = 2612+14=26,差距为 ∣27−26∣=1|27 − 26| = 1∣27−26∣=1。
数据规模与约定
- 对 20%20\%20% 的数据,n≤50n \leq 50n≤50。
- 对全部的测试数据,保证 1≤n≤1031 \leq n \leq 10^31≤n≤103,1≤ai≤1091 \leq a_i \leq 10^91≤ai≤109。
思路:方法1,双指针枚举,时间复杂度为o(n^3),
使用双指针,将n个人分为两个区间,若左侧区间和<右侧区间和,那么左侧区间和+a[l–],否则右侧区间和+a[r++].然后实时更新最小值即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[1005];
int n;
ll mmin=LLONG_MAX;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<n;i++)
{
for(int j=i+1;j<=n;j++)
{
ll suml=a[i];
ll sumr=a[j];
ll l=i-1;
ll r=j+1;
mmin=min(mmin,abs(sumr-suml));
while(l>=1&&r<=n)
{
if(suml>sumr)
{
sumr+=a[r++];
mmin=min(mmin,abs(sumr-suml));
}
else
{
suml+=a[l--];
mmin=min(mmin,abs(sumr-suml));
}
}
while(l>=1)
{
suml+=a[l--];
mmin=min(mmin,abs(sumr-suml));
}
while(r<=n)
{
sumr+=a[r++];
mmin=min(mmin,abs(sumr-suml));
}
}
}
cout<<mmin;
return 0;
}
思路:方法2,前缀和+二分查找,时间复杂度O(n^2*logn)
求出前缀和,然后计算并存放所有的前缀和组合,枚举每一个前缀和,用库函数lower_bound()进行查找与其差值最小的数,并实时更新总体差值最小的数,需要注意的是,对于枚举查找1-i之间的前缀和,在之前需要删除所有以1开头的前缀和的组合,防止人员重复。
//#include <bits/stdc++.h>
//using namespace std;
//
//typedef long long ll;
//ll a[1005];
//int n;
//ll ans=LLONG_MAX;
//
//multiset<ll> s;
//
//int main(void)
//{
// cin>>n;
// for(int i=1;i<=n;i++)
// {
// cin>>a[i];
// a[i]=a[i-1]+a[i];
// }
// for(int i=0;i<n;i++)
// {
// for(int j=i+1;j<=n;j++)
// {
// s.insert(a[j]-a[i]);
// }
// }
//
// long long sumr=0;
// for(int i=1;i<=n;i++)
// {
//
// //删除所有以i开头的前缀和,防止人员重复
// for(int j=i;j<=n;j++)
// {
// auto p=s.find(a[j]-a[i-1]);
// s.erase(p);
// }
// //寻找前半段的值
// for(int j=1;j<=i;j++)
// {
//
// ll k=a[i]-a[j-1];
//
// auto p=s.lower_bound(k);
// //下面求 第一个大于等于k的值和k的差值 和 第一个比k小的值和k的差值
//
// //如果找到p,那么判断*p和k的差值与ans的大小
// if(p!=s.end())
// {
// ans=min(ans,abs(*p-k));
// }
// //如果p不是第一个 那么找p前一个比较
// if(p!=s.begin())
// {
// p--;
// ans=min(ans,abs(*p-k));
// }
// }
//
// }
//
// cout<<ans;
// return 0;
//}
5141

被折叠的 条评论
为什么被折叠?



