蓝桥杯拔河,考察双指针,前缀和,区间处理
这题解法很多,但是值得学习的我觉得还是双指针。数据可能爆炸所以建议开long long
代码
#include <bits/stdc++.h>
using namespace std;
long long rest=100000000;
long long abss(long long a,long long b)
{
long long fin=a>b?a-b:b-a;
return fin;
}
long long minn(long long a,long long b)
{
long long fin=a>b?b:a;
return fin;
}
int main()
{
//双指针思路,定义l,r。l代表右队伍的左边界,r代表左队伍的右边界,每次判断左右队伍的和,那边少就在那边加入新的同学
int n;
cin>>n;
long long a[1024]={0};
for(int i=1;i<n;i++)
{
cin>>a[i];
}
// 枚举区间
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
rest=minn(rest,abss(a[j],a[i]));//初始都只有一个先判断一下,防止一开始就是最小值,加不加都可以,加了更严谨
long long minr=a[i];//设置左右两边的力量总和
long long minl=a[j];
int ll=i-1;
int rr=j+1;设置边界条件,这里开始要进行差值判断了
while(ll>=0&&rr<n)//开始进行人数的加入,因为要获取最小的差值,所以遍历所有可能
{
//判断那边人更少,要保证人数连续,而且差值最小,所以要判断左边加还是右边加,
//其实就是遍历所有最小差值的可能性。
if(minr<minl)
{
minr+=a[ll];
ll--;
}
else
{
minl+=a[rr];
rr++;
}
rest=minn(rest,abss(minl,minr));//每次判断完都要进行差值判断
}
//当一边枚举完另一遍没有的时候,将另一边枚举完
while(ll>=0)
{
minr+=a[ll];
ll--;
rest=minn(rest,abss(minl,minr));
}
while(rr<n)
{
minl+=a[rr];
rr++;
rest=minn(rest,abss(minl,minr));
}
}
}
//最后建议取整函数和取最小值函数建议自己写,因为数据要开long long 而自带的min函数和abs函数返回类型是int会报错。可能强制转换可以实现吧,但我没想过。
cout<<rest;
return 0;
}
思路在题目中,还有一个暴力解法和前缀和,
为了方便理解我说一个四重暴力
for 左边团队的左边界
for 左边团队的右边界
for 右边团队的左边界
for 右边团队的右边界
每次取不同的区间,最后在进行差值判断。
其他两种方法都是在优化
暴力满分题解
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
const int N=1e3+10;
int n,js=1,a[N],check[N*N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){//我们先通过二层循环,暴力出每一种选法
int s=0;
for(int j=i;j<=n;j++){
s+=a[j];
check[js++]=s;
}
}//可能有谁都不选的选法,check数组从0开始
sort(check,check+js);
//将他们从小到大排序,力量值之和差距最小的一定是相邻的两种选法
//不用管区间选择重叠的情况,重叠部分相当于两种选法都没选重叠部分
int ans=1e12+10;
for(int i=1;i<js;i++)ans=min(ans,check[i]-check[i-1]);
cout<<ans;
return 0;
}
简单解释一下
for(int i=1;i<=n;i++){//我们先通过二层循环,暴力出每一种选法
int s=0;
for(int j=i;j<=n;j++){
s+=a[j];
check[js++]=s;
}
这串代码的意思是将所有区间记录下来,对所有区间排序后,对于区间重复可以忽略因为,因为两者都有,可以都不要。排序后差值最小的应该是相邻的。其实这个考察的就是前缀和,加区间处理。
另一种解法就是利用前缀和来优化
set<int> s;
for(int i = 1;i<=n;i++) {
cin>>a[i];
a[i]+=a[i-1];//构造前缀和
}
for(int i = 1;i<=n;i++){
for(int j = i;j<=n;j++){
s.insert(a[j]-a[i-1]);//枚举右区间所有情况先加入set中
}
}
上面用的map容器原理,下面这个用的set,其实对于重复的区间我们可以不考虑,但也不影响,
好处是set容器自动去重,减少时间复杂度。
下面是原版的代码,但是我觉得完全没有必要,去考虑区间重合问题,但是放出来学习一下。
#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
using namespace std;
#define ll long long
struct interval
{
ll value, l, r;
};
vector<ll>v, v_sum;
vector<interval> v_sum_interval;
ll n, a1, sum = 0, min_distance = numeric_limits<ll>::max(), k1, a2 = 0;
interval kk;
int main() {
cin >> n;
v_sum.push_back(0);
for (ll i1 = 0; i1 < n; i1++) {
cin >> a1;
sum += a1;
v_sum.push_back(sum);
v.push_back(a1);
}
for (ll i1 = 1; i1 <= n; i1++) {
for (ll i2 = 0; i2 < i1; i2++) {
kk.value = v_sum[i1] - v_sum[i2],kk.l=i2+1,kk.r=i1;
v_sum_interval.push_back(kk);
}
}
sort(v_sum_interval.begin(), v_sum_interval.end(), [](const interval& i1,const interval& i2) {return i1.value < i2.value; });
for (ll i1 = 0; i1 < v_sum_interval.size() - 1; i1++) {
if (v_sum_interval[i1 + 1].l > v_sum_interval[i1].r||v_sum_interval[i1 + 1].r < v_sum_interval[i1].l)
min_distance = min(min_distance, v_sum_interval[i1 + 1].value - v_sum_interval[i1].value);
}
cout << min_distance;
}