洛谷 P1031 [NOIP2002 提高組] 均分紙牌 題解

zhuluoan發表於2024-05-09

題目簡述

有 $N$ 堆紙牌,編號分別為 $1,2,\ldots,N$。每堆上有若干張,但紙牌總數必為 $N$ 的倍數。可以在任一堆上取若干張紙牌,然後移動。

移牌規則為:在編號為 $1$ 堆上取的紙牌,只能移到編號為 $2$ 的堆上;在編號為 $N$ 的堆上取的紙牌,只能移到編號為 $N-1$ 的堆上;其他堆上取的紙牌,可以移到相鄰左邊或右邊的堆上。

現在要求找出一種移動方法,用最少的移動次數使每堆上紙牌數都一樣多。

題目分析

首先定義 $\texttt{avg}$ 為紙牌總數的平均數。如果要使每堆紙牌的數量一樣多的話,那麼經過移動後要使 $A_i=\texttt{avg}$。

對於第一堆牌,它只能由第二堆牌進行索取,或者進行給予。設 $x=A_i-\texttt{avg}$。如果 $x>0$,那麼就把多的給第二堆。如果 $x<0$,不管怎麼移動,必然有一步是第二堆給第一堆 $\lvert x \rvert$ 張牌。上述兩種情況答案需要加 $1$。如果 $x=0$,就不用管了。

此時我們發現第一堆牌已經符合條件了,那麼第一堆就可以直接忽略了,此時第二堆就變成了第一堆,繼續重複上述步驟。

如何說明這樣做是最優的呢?我們發現在上述過程中,每一步都是必須的,沒有多餘的步驟,所以其必然是最優解,這也是貪心思想的體現。

如果移牌的過程中出現負數怎麼辦?對於這個問題,可以發現移牌的步驟的順序不是固定的,這樣,我們就可以每次移動不會出現負數的牌堆,這樣的方案是一定存在的。

程式碼

#include<iostream>
using namespace std;
int n,a[100001],sum,s,ans;
int main()
{
	cin>>n;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
		sum+=a[i];
	}
	sum/=n;
	for(int i=0;i<n-1;i++)
	{
		if(a[i]!=sum)
		{
			a[i+1]+=(a[i]-sum);
			ans++;
		}
	}
	cout<<ans;
    return 0;
}

相關文章