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小呀,所以這樣就能保證取模後的餘數小,這樣操作就少了。

取模後怎麼做呢?

我們枚舉每個素因子,然後每次對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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章