A Simple Stone Game HDU - 6237(分解质因子+思维)

A Simple Stone Game HDU - 6237

After he has learned how to play Nim game, Bob begins to try another stone game which seems much easier.

The game goes like this: one player starts the game with N piles of stones. There is ai stones on the ith pile. On one turn, the player can move exactly one stone from one pile to another pile. After one turn, if there exits a number x(x>1) such that for each pile bi is the multiple of x where bi is the number of stone of the this pile now), the game will stop. Now you need to help Bob to calculate the minimum turns he need to stop this boring game. You can regard that 0
is the multiple of any positive number.
Input
The first line is the number of test cases. For each test case, the first line contains one positive number N(1≤N≤100000), indicating the number of piles of stones.

The second line contains N positive number, the ith number ai(1≤ai≤100000) indicating the number of stones of the ith pile.

The sum of N of all test cases is not exceed 5∗105
.
Output
For each test case, output a integer donating the answer as described above. If there exist a satisfied number x initially, you just need to output 0
. It’s guaranteed that there exists at least one solution.
Sample Input

2
5
1 2 3 4 5
2
5 7

Sample Output

2
1

题意:

现在有n堆石子,且已知每堆石子的数量,每次操作你可以从任一堆中取一颗石子放到另一堆中,问最少操作几次使得每一堆石子的数量均是某一个数的倍数。即n个数都有一个公因子,a[i]%x==0 (x > 1)

分析:

首先我们考虑如果对于每一个数a[i] % x = 0,则这n个数的总和sum % x = 0,因为相当于有n个x相加了嘛,结果必然是n的倍数。

那么这个时候我们就能想到,sum的因子,就可以作为这n个数的公因子,即x

但是sum会很大,那么所有的因子数量众多,一一枚举是不可能的

我们知道题目要求的只是含有公因子即可,那么我们就找最本质的因子,即素因子,因为每个因子都可由素因子乘积表示,所以,以某一因子x作为公因子,和以构成x的素因子p作为公因子,这两个之间只是差了整倍数的关系,而整倍数其实是不需要考虑的,对我们没有影响

为什么?主要原因是我们的做题方法,既然我们想要让每个数a[i] % x = 0,即我们想看看a[i]是不是x的倍数,所以我们取模,如果为0说明是,就不管了,不是的话,余数就是我们需要补充的或减去的,由于是取模,所以如果a[i] % x = 0,那么a[i] % p = 0,反之如果a[i] % x != 0,那么a[i] % p会更好,因为p比x小呀,所以这样就能保证取模后的余数小,这样操作就少了。

扫描二维码关注公众号,回复: 3768048 查看本文章

取模后怎么做呢?

我们枚举每个素因子,然后每次对n个数取模,0就不用管了,对于所有非零的,我们对所有余数排好序,显然我们把大的余数不上就行,因为大的余数就更加接近取模的因子,补的少,操作就少,所以对于排好序的余数序列,我们从后往前即从大到小遍历操作,但是我们应该选取那个小的数减去去补上大的数呢,我们根本不需要选,因为这些数的总和不会变,数字变化只是内部变化,所以我们就假设选择了某个较小数,把最大的补成了枚举的素因子的大小,这样某个较小数肯定就相应减少一些,我们不要知道,我们只需要知道现在我们凑得了一个素因子,所以之前记录下这些余数的总和sum,用sum减去一个这个因子就行了,只要sum一直减到0就说明全部都凑成这个素因子了,对于操作次数就是每次加上要操作的数和所枚举素因子的差值啦。
枚举过程不断更新答案就行了、

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;

int n;
ll a[maxn];
vector<ll> vec;
vector<ll> mp;

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        vec.clear();
        ll sum = 0;
        for(int i = 0; i < n; i++){
            scanf("%lld",&a[i]);
            sum += a[i];
        }
        for(ll i = 2; i * i <= sum; i++){
            if(sum % i == 0){
                vec.push_back(i);
                while(sum % i == 0){
                    sum /= i;
                }
            }
        }//分解sum质因子
        if(sum > 1) vec.push_back(sum);
        sort(vec.begin(),vec.end());
        ll ans = 1e18;
        for(int i = 0; i < vec.size(); i++){
            sum = 0;
            mp.clear();
            for(int j = 0; j < n; j++){
                int tmp = a[j] % vec[i];
                if(tmp){
                    mp.push_back(tmp);
                    sum += tmp;//取模得到余数并记录总和
                }
            }
            sort(mp.begin(),mp.end());//排好序从大到小遍历
            ll cnt = 0;
            for(int j = mp.size() - 1; j >= 0; j--){
                cnt += vec[i] - mp[j];//次数就是差值
                sum -= vec[i];//凑成一个素因子,总和减去
                if(sum <= 0) break;//小于等于0就说明全部凑完了
            }
            ans = min(ans,cnt);//每次更新答案
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/82826735