题目大意:有n个银行编号为1-n,n视作与1相邻,每个银行有 ai(−1e9≤ai≤1e9) 的存款。有一种操作,相邻编号的银行可以转任意多的钱。使每个银行的存款都为0的最小操作数是多少。保证 ∑ni=1ai=0
题解:
1.每个相邻的银行之间最多发生一次转移,因此最多转移次数为n次。
2.若能分成k段不相交的和为0的区间,则只有区间内部的银行需要转移,而相邻的区间之间不需要转移,即减少了k次转移。
3. 因此只需要将银行分成最多的和为0的区间,在区间内部转移即可。问题变成求最大的不相交和为0的区间数。
和为0的区间实际上是两段前缀和相同的区间的差,因此只要统计相同的前缀和的个数,每一种前缀和对应的是一个和为0的区间划分。若有k个前缀和相同,由于是环形的,因此该划分分成了k个和为0的区间。找到最大的划分方案数k,那么n-k就是答案。
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 200020;
const int inf = 0x3f3f3f3f;
typedef long long ll;
vector<pair<ll,int> > HasH[maxn];
ll a[maxn];
int main(){
int n;
scanf("%d",&n);
for(int i= 0;i < n;i++) scanf("%I64d",&a[i]);
int ans = n;
int k = (a[0]%maxn+maxn)%maxn;
HasH[k].push_back(pair<ll,int>(a[0],1));
for(int i = 1;i < n;i++){
a[i] += a[i-1];
k = (a[i]%maxn+maxn)%maxn;
int flag = 1;
for(int j = 0;j < HasH[k].size();j++){
pair<ll,int> t = HasH[k][j];
if(t.first == a[i]){
HasH[k][j].second++;
break;
}
}
HasH[k].push_back(pair<ll,int>(a[i],1));
}
int cnt = 0;
for(int i = 0;i < maxn;i++){
for(int j = 0;j < HasH[i].size();j++){
cnt = max(HasH[i][j].second,cnt);
//printf("%d\n",HasH[i][j].second);
}
}
printf("%d\n",ans-cnt);
return 0;
}