題目描述
有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;
}