首先,来复习一下贪心,对一个问题的每个过程求局部最优解,从而最后推导出全局最优解。
上例题。
有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若干张纸牌,然后移动。
移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。
对这个题目,首先应该想到怎么移动纸牌。我们可以从移动纸牌的张数入手。
由当前纸牌数减去平均数,就可以得到要移动的纸牌数,正数是多了几张,负数是少几张。最终目的是使所有数都变成0.
cin>>n;
ave=0;step=0;
for (i=1;i<=n;++i)
{
cin>>a[i];
ave+=a[i]; //读入各堆牌张数,求总张数ave
}
ave/=n; //求牌的平均张数ave
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; //移牌步数计数
++i; //对下一堆牌进行循环操作
while (a[i]==0&&i<j) ++i; //过滤移牌过程中产生的0
}
cout<<step<<endl;
在这之中有一个弯,就是如果一个数是1,-6,而第二个数是1,-5,都欠着牌,而且后面的不够补到前面的,怎么办。
其实照样补过去,第二个变成-11,-11。由后面多的总会补回来。
动态规划预习
动态规划,也是一种最优解的算法。他将问题实例分解为更小的/相似的子问题,并存储子问题的解,使得每个子问题只求解一次,最终获得原问题的答案。
他与贪心法类似,都是将问题实例归纳为更小的、相似的子问题,并通过求解子问题产生一个全局最优解,但动态规划通过求解局部子问题的最优解来达到全局最优解。
如要求一个问题的最优解,而且该问题能够分解成若干个子问题,并且小问题之间也存在重叠的子问题,则考虑采用动态规划。