专栏文章

题解:P1031 [NOIP 2002 提高组] 均分纸牌

P1031题解参与者 13已保存评论 19

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
19 条
当前快照
1 份
快照标识符
@miq6h9y6
此快照首次捕获于
2025/12/03 23:44
3 个月前
此快照最后确认于
2025/12/03 23:44
3 个月前
查看原文

题解:P1031 [NOIP 2002 提高组] 均分纸牌

题意

给出 nn,然后给出 nn 个数。这 nn 个数组成了一个序列(我们不妨将它们设为 a1a_{1}ana_{n})。现在可以对这个序列中的每一个数 aia_{i} 进行以下三种操作,使得序列中每一个数字都相等:
  • ii11 时,可以将 aia_{i} 中的一部分移动到 ai+1a_{i+1},也就是 a2a_{2}
  • iinn 时,可以将 aia_{i} 中的一部分移动到 ai1a_{i-1}
  • 1<i<n1 < i < n 时,可以将 aia_{i} 中的一部分移动到 ai+1a_{i+1}ai1a_{i-1}
显然,这里的“移动”就是使得 aia_{i} 减小一个数,使得 ai+1a_{i+1}ai1a_{i-1} 加上同一个数。
反过来,aia_{i} 也可以接受其他数的转移。

思路

贪心
首先,由于要求每个数字相等,那么所有数字的总和一定可以被 nn 整除。整除后的这个数字(也是这个序列的平均数),我们暂且把它叫做 numnum
此时,对于序列中的每一个数 aia_{i},只有三种情况:
  1. 如果 ai>numa_{i} > num,则 aia_{i} 多出来的部分(ainuma_{i} - num)应该被转移到临近的数上;
  2. 如果 ai<numa_{i} < num,则 aia_{i} 缺少的部分(numainum - a_{i})应该由邻近的数转移而来;
  3. 如果 ai=numa_{i} = num,则不需要进行任何操作。
那么,这个“临近的数”,应该是哪一侧的数呢?
让我们回到这个序列上。
对于 a1a_{1},因为它左侧没有数,所以它进行的一切操作都只能依靠 a2a_2。而当我们对它进行了操作后,a1a_{1} 显然就和 numnum 相等了。
这时,对于 a2a_{2},因为它左侧的 a1a_{1} 已经与 numnum 相等了,所以它进行的一切操作都只能依靠 a3a_{3}。而当我们对它进行了操作后,a2a_{2} 显然就和 numnum 相等了。
以此类推,我们就解决了刚才的问题:对于 aia_{i},因为它左侧的 ai1a_{i-1} 已经与 numnum 相等了,所以它进行的一切操作都只能依靠 ai+1a_{i+1}。而当我们对它进行了操作后,aia_{i} 显然就和 numnum 相等了。
所以,对于序列中的每一个数 aia_{i},变成了这三种情况:
  1. 如果 ai>numa_{i} > num,则 ai+1=ai+1+ainuma_{i+1} = a_{i+1} + a_{i} - numai=numa_{i} = num
  2. 如果 ai<numa_{i} < num,则 ai+1=ai+1num+aia_{i+1} = a_{i+1} - num + a_{i}ai=numa_{i} = num
  3. 如果 ai=numa_{i} = num,则不需要进行任何操作。
对于前两种情况,不难发现,其实它们进行的操作是一样的。
CPP
for(ll i=1;i<n;i++){
	if(a[i]==num) continue;//相等则不进行操作
	a[i+1]+=(a[i]-num);//否则进行移动
	a[i]=num;
	ans++;//答案增加
}

代码实现

CPP
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,ans,a[110],num;
int main(){
	cin>>n;
	for(ll i=1;i<=n;i++){
		cin>>a[i];
		num+=a[i];//求序列和
	}num/=n;//求平均数
	for(ll i=1;i<n;i++){
		if(a[i]==num) continue;//相等则不进行操作
		a[i+1]+=(a[i]-num);//否则进行移动
		a[i]=num;
		ans++;//答案增加
	}cout<<ans;
	return 0;//好习惯
}//完结撒花QAQ

评论

19 条评论,欢迎与作者交流。

正在加载评论...