【CodeForces】675C - Money Transfers(思维)

题目链接

题意:p城有n家银行,第i个银行与第i+1,i-1个银行相邻,特殊的,1与n相邻,即这些银行形成了一个环。你在n家银行都有账户,并且n个账户上的钱数或正或负,但总和为0,现在你需要通过转账,将你的n个账户余额全部更改为0,求最小操作数。

思路:假设我们可以将这n家银行分成k段连续的区间,每个区间内银行余额和为0,那么我们只需对每个区间内进行移动即可,若第i个区间长度为   b i   那么移动次数即为 b i 1 ,设总移动数为 c n t ,则有

c n t = i = 1 k b i k = n k

对于k,我们可以用前缀和求出,具体方法是求出前缀和中出现次数最多的那个数字m出现的次数cnt即可,证明如下:

  1. 各前缀和都不同,那么就只有 [ 1 n ] 一个连续子集的和为零,cnt = 1。
  2. 当前缀和出现重复时,cnt = 出现最多的前缀和的出现次数。
    举个例子:
    下标: 1 2 3 4 5 6 7 8 9
    余额: 1 2 0 1 -1 2 1 -3 -3
    前缀和: 1 3 3 4 3 5 6 3 0
    对于该序列,3出现的四次,最多。 [ 3 , 3 ] 区间和为0,操作0次, [ 4 , 5 ] 区间和为0,操作1次, [ 6 , 8 ] 区间和为0,操作2次,剩下的1,2,9和为0,操作2次,共操作5次。满足ans = n - cnt。

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;   
map<ll ,int > mp;
int main(int argc, char const *argv[])
{
    int n;
    scanf("%d",&n);
    ll sum = 0,x;
    int cnt = 1;
    for(int i=0;i<n;++i){
        scanf("%I64d",&x);
        sum += x;
        mp[sum]++;
        cnt = max(cnt,mp[sum]);
    }
    printf("%d\n",n-cnt);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41009682/article/details/82151421