在这里记录一个比较典型的贪心算法题目。
题目描述:现有n堆纸牌,编号分别为1,2,3,…,n。可以在任一堆上取若干张纸牌,然后移动。
移牌规则:第一堆的牌只能移到第二堆,第n堆只能移到第(n-1)堆,其余编号的牌可以移到左右相邻的堆数里。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
输入格式:
键盘输入文件名。文件格式:
N(N 堆纸牌,1 <= N <= 100)
A1 A2 … An (N 堆纸牌,每堆纸牌初始数,l<= Ai <=10000)
输出格式:输出至屏幕。格式为:
所有堆均达到相等时的最少移动次数。
输入输出样例
输入样例#1:
4 9 8 17 6
输出样例#1:
算法分析:这道题目的关键是找到平均值这个突破点。就拿样例来说,四堆牌的平均值为10,用每一堆的牌数减去平均值就得到-1,-2,7,-4。这样题目就转化为怎样移动使得每堆牌的数值都为0。即每一堆不为零的值都传给右边相邻的一堆,直到每堆牌对应的值都为0为止。如:-1传给-2就得到(0,-3,7,-4);-3传给7就得到(0,0,4,-4);-4传给4就得到(0,0,0,0)。即一共移动三次就达到了题目的要求,此时移动次数最少。
算法实现基本思路:求出n堆纸牌的平均值,定义两个指针i和j,分别从两侧逐一过滤。
#include<bits/stdc++.h>
using namespace std;
int a[100001];
int main()
{
int n,ave=0,step=0,i,j;
cin>>n;
for(i=1;i<=n;i++)
{
cin>>a[i];
ave+=a[i];//计算总数
}
ave/=n;//算出平均值
for(i=1;i<=n;i++) a[i]-=ave;
i=1;j=n;//定义两个指针
while(a[i]==0&&i<n) i++;//筛掉左边的0
while(a[j]==0&&j>1) j--;//筛掉右边的0
while(i<j)
{
a[i+1]+=a[i];//将第i堆牌移到第(i+1)堆中去
a[i]=0;//第i堆牌移走后变为0
step++;//移动步数+1
i++;//对下一堆牌进行循环操作
while(a[i]==0&&i<j) i++;//过滤移牌过程中产生的0
}
cout<<step<<endl;
return 0;
}