题目:
有N堆纸牌,编号分别为1,2,…,n。每堆上有若干张,但纸牌总数必为n的倍数.可以在任一堆上取若干张纸牌,然后移动。移牌的规则为:在编号为1上取的纸牌,只能移到编号为2的堆上;在编号为n的堆上取的纸牌,只能移到编号为n-1的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。例如:n=4,4堆纸牌分别为:① 9 ② 8 ③ 17 ④ 6 移动三次可以达到目的:从③取4张牌放到④ 再从③区3张放到②然后从②去1张放到①。
输入:
4
9 8 17 6
输出:
3
思路:
让你把每堆纸牌都平分成 总牌数/N 张纸牌,要求用最少的移动次数。
首先,想一个局部解,比如,从左边的一堆开始,即为当前状态,那么当前状态的最优解就是从第二堆里一次就拿够牌,或把多的牌放在第二堆,然后再移第二堆,第三堆……
然后,检测该局部策略是否能够作为全局的贪心策略,很显然,即使是在现实生活中,也是这几步,只不过变了变顺序,你可能会先移动,牌最多的,多的牌分给,牌少的,其实只要牌不够平均数,那么至少要有一步,才能凑够平局数,所以该策略是满足贪心策略。
当然会有一个特殊情况,不过也是正确的,如n=3,① 1② 7③ 22,这时每堆要十张牌,当从第二堆往第一堆拿9张牌时,第二堆变成了-2,仍然按该策略做下去,那么第二堆从第三堆拿12张牌,现在就都是十张牌了,共用了两步,实际情况也是两步,只不过顺序不同。
代码如下:
#include <iostream> using namespace std; int main() { int dui[100]; int n; int sum=0,p,step=0; cin>>n; for(int i=0;i<n;i++){ cin>>dui[i]; sum+=dui[i]; } p=sum/n; for(int i=0;i<n;i++){ if(dui[i]!=p){ dui[i+1]+=dui[i]-p; dui[i]=p; step++; } } cout<<step; return 0; }