简单记录一下这道算是自己写出来的第一道1400分题目(暴力+前缀和)
目录
一、题目
输入
10
5
5 1 1 1 1
1 1 5 1 1
1 1 1 1 5
6
1 2 3 4 5 6
5 6 1 2 3 4
3 4 5 6 1 2
4
4 4 4 4
4 4 4 4
4 4 4 4
5
5 10 5 2 10
9 6 9 7 1
10 7 10 2 3
3
4 5 2
6 1 4
1 8 2
3
10 4 10
8 7 9
10 4 10
7
57113 65383 19795 53580 74452 3879 23255
12917 16782 89147 93107 27365 15044 43095
33518 63581 33565 34112 46774 44151 41756
6
6 3 1 8 7 1
10 2 6 2 2 4
10 9 2 1 2 2
5
5 5 4 5 5
1 6 3 8 6
2 4 1 9 8
10
1 1 1 1 1001 1 1 1001 1 1
1 1 1 1 1 1 2001 1 1 1
1 1 1 1 1 1001 1 1 1 1001
输出
1 1 2 3 4 5 5 6 1 2 3 4 -1 -1 1 1 3 3 2 2 -1 1 2 3 4 5 7 3 6 1 1 2 2 1 2 3 4 5 5 1 5 6 7 8 10
二、思路和代码
一开始我的想法是找到两个点,使数组长度分成3段,每一段进行判断,满足条件就是答案。我本来想贪心,找到每个下标里3个人最大价值的那个数组开始递增,但是这样要处理的条件就太多了,最后放弃。
想了1个多小时,最后想从暴力角度找思路,惊奇的发现暴力枚举一共也才6种情况,即遍历3个人在第一段的情况,再遍历另外两个人在第二段的情况,最后判断。当6种情况都不满足时,则没有合法答案,输出-1。
因为求的是每一段区间的总和,所以使用前缀和优化一下。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<vector>
#define int long long
using namespace std;
const int N = 2e5+10,INF=0x3f3f3f3f;
typedef pair<int,int> PII;
int T;
int n;
int sum[3][N];
int a[N];
int b[N];
int c[N];
int l[3],r[3];
void solve(){
cin>>n;
int tot=0;
for(int i=1;i<=n;i++){
cin>>a[i];
tot+=a[i];
sum[0][i]=sum[0][i-1]+a[i];
}
for(int i=1;i<=n;i++) cin>>b[i],sum[1][i]=sum[1][i-1]+b[i];
for(int i=1;i<=n;i++) cin>>c[i],sum[2][i]=sum[2][i-1]+c[i];
int t = (tot+2)/3;
for(int i=0;i<3;i++) {//遍历3种开始的情况
int m=0;
for(int j=1;j<=n;j++){//找到一种情况使某个人满足条件,第一个人一定能找到一种状态满足条件
if(sum[i][j]-sum[i][0]>=t){
l[i]=1,r[i]=j;
m=j+1;
int k=1;
while((i+k)%3!=i){//剩余两种情况
int x=(i+k)%3;
l[x]=INF,r[x]=INF;
for(int d=m;d<=n;d++){
if(sum[x][d]-sum[x][m-1]>=t){
l[x]=m,r[x]=d;
//判断第三个人是否满足
int y = (3-i-x);//第三个人
if(sum[y][n]-sum[y][d]>=t){
l[y]=d+1,r[y]=n;
for(int i=0;i<3;i++) cout<<l[i]<<" "<<r[i]<<" ";
puts("");
return;
}else break;
}
}
k++;//下一种情况
}
break;//进行下一种情况
}
}
}
cout<<-1<<endl;//没有满足条件的情况
}
signed main(){
cin>>T;
while(T--){
solve();
}
return 0;
}