NOIP 2002 均分紙牌(貪心||模擬)

題目描述

有N堆紙牌,編號分別爲1,2,...,N。每堆上有若干張,但紙牌總數必爲N的倍數。可以在任一堆上取若干張紙牌,然後移動。
移牌規則爲:在編號爲1的堆上取的紙牌,只能移到編號爲2的堆上;在編號爲N的堆上取的紙牌,只能移到編號爲N-1的堆上;其他堆上取的紙牌,可以移到相鄰左邊或右邊的堆上。 
現在要求找出一種移動方法,用最少的移動次數使每堆上紙牌數都一樣多。 
例如N=4,4堆紙牌數分別爲: 
  ① 9 ② 8 ③ 17 ④ 6 
      移動3次可達到目的: 
  從③取4張牌放到④(9 8 13 10)->從③取3張牌放到②(9 11 10 10)->從②取1張牌放到①(10 10 10 10)。 

 

輸入

每個測試文件只包含一組測試數據,每組輸入的第一行輸入一個整數N(1<=N<=100),表示有N堆紙牌。 
接下來一行輸入N個整數A1 A2...An,表示每堆紙牌初始數,1<=Ai<=10000。 

 

輸出

對於每組輸入數據,輸出所有堆均達到相等時的最少移動次數。 

 

分析:n堆的紙牌最多移動n-1次就可以使每堆紙牌被均分。用貪心的思想,先預處理出每堆紙牌和平均數的差值,然後從左往右把這些值加起來(可以視爲將這些紙牌不斷放在一起),如果下一堆紙牌加上前面的已經爲0了,就說明這堆紙牌不用再進行操作,(可以視爲它和它前面的堆已經被均分了),否則的話,就讓次數,如果累加到一堆的時候不爲0,就讓次數+1。其實這可以看做是一個模擬移動紙牌的過程。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e2+7;
const int inf=0x3f3f3f3f;
int a[maxn];

int main()
{
    int N,sum=0;
    scanf("%d",&N);
    for(int i=0;i<N;i++)
    {
        scanf("%d",&a[i]);
        sum+=a[i];
    }
    int ave=sum/N;
    for(int i=0;i<N;i++)
    {
        a[i]-=ave;
    }
    int ans=0;
    for(int i=0;i<N;i++)
    {
        if(a[i]==0) continue;
        a[i+1]+=a[i];
        ans++;
    }
    printf("%d\n",ans);
    return 0;
}

 

在網上看到一個大神用的模擬做,%%%。

思路是:
1. 先求出前綴和(求出前i堆紙牌的總和),並求出每堆紙牌的平均值。

2. 然後從左到右,根據當前的前i堆的和sum(i),與前i堆的期望和(即i*ave),進行比較,如果多了,則向後面移動一次紙牌(更新a[i], sum[i], a[i+1])。一輪過後,使得每堆無需再向後調整(有點類似快排的思想,一個區域內的部分達到穩定)。

3. 再從右向左,根據每堆的紙牌數a[i],與每堆期望紙牌數ave比較,如果多了,則向前移動紙牌(更新a[i], sum[i-1], a[i-1])
其實也可以不更新sum了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e2+7;
int a[maxn];
int sum[maxn];

int main()
{
    int N;
    scanf("%d",&N);
    for(int i=1;i<=N;i++)
    {
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    int ave=sum[N]/N;
    int ans=0;
    for(int i=1;i<=N;i++)
    {
        if(sum[i]>i*ave){
            int d=sum[i]-i*ave;
            sum[i]-=d;//可以不更新sum數組了,因爲前i堆的不會再用了
            a[i+1]+=d;
            a[i]-=d;
            ans++;
        }
    }
    for(int i=N;i>=1;i--)
    {
        if(a[i]>ave){
            int d=a[i]-ave;
            a[i-1]+=d;
            a[i]-=d;
            sum[i-1]+=d;//這裏也可以不再更新了,因爲是用的每堆的牌數和ave進行比較
            ans++;
        }
    }
    printf("%d\n",ans);
    return 0;
}

 

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