【算法学习笔记】25.贪心法 均分纸牌问题的分析

贪心法:

贪⼼算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。

贪心算法不是对所有问题都能得到整体最优解,关键是贪⼼心策略的选择,选择的贪⼼策略必须具备⽆后效性,即某个状态以后的过程不会影响以前的状态,只与当前状态有关。

太概念化了。总结起来三点:

可行性:必须满足问题的约束

局部最优:当前步骤中所有可行的选择里最佳的局部选择。

不可取消:选择一旦做出,后面的步骤就无法改变。

问题要具有贪心选择性:问题的最优解可以通过一系列的局部最优选择来达到。(最重要的一步,决定这个问题是否可以用贪心法来解决,此处的解决特指找到最优解)。

最优子结构性质:指一个问题的最优解一定要包含子问题的最优解。

贪心和DP的差别在哪呢,首先他萌确实都有最优子结构的性质,但是DP通常是以自底向上的方式解决各个子问题(如22中的整装待发问题就是从底部的每一层逐渐建立起那个二维数组),而贪心的方法通常是自顶向上的。

 

均分纸牌问题的分析:

均分纸牌问题:有 N 堆纸牌,编号分别为 1,2,…, N。每堆上有若干张,但纸牌总数必为 N 的倍数。可以在任一堆上取若于张纸牌,然后移动。

  移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。

  现在要求找出一种移动方法,用最少的移动次数使每堆上纸牌数都一样多。

  例如 N=4,4 堆纸牌数分别为:

  ① 9 ② 8 ③ 17 ④ 6

  移动3次可达到目的:

  从 ③ 取 4 张牌放到 ④ (9 8 13 10) -> 从 ③ 取 3 张牌放到 ②(9 11 10 10)-> 从 ② 取 1 张牌放到①(10 10 10 10)。

 先放代码在分析吧,代码比较短。

int main(){
    int N;
    int pokers[MAX];
    cin>>N;
    int total = 0;
    for (int i=0; i<N; i++) {
        cin>>pokers[i];
        total+=pokers[i];
    }
    int avg = total/N,times=0;
    for(int i=0;i<N;i++){
        if(pokers[i]!=avg){
            pokers[i+1] -= avg - pokers[i];
            times++;
        }
    }
    cout<<times<<endl; 
}

可以看到最核心的那个循环的思想是这样的:

从第一堆牌开始处理,如果第一堆牌整好是avg那么就放在一边不管了。

如果第一堆牌不是avg,那么就要把第二堆牌(合法的移动只有从2移到1,这也是这个算法的精髓之处)移动几张到第一堆,恰好使第一堆等于avg,从而只考虑第二堆开始到第N堆为止这些堆如何搞的子问题。然后依次递归下去。

这里的一个小技巧是认为牌数可以为负数,这样才能继续下去。综上,这个步骤是合理的。但是看不出来是最优的。可见,贪心法确实是比较容易实现,因为比较符合人类直觉,但是不好证明。

再反过来看一下前面提到的几点,可行性满足,不可取消,每一次操作都是直接赋值,局部最优,当前情况下,只能从右往左移动,且贪心地想尽快让第一堆满足约束。

至于为什么是最优解,(最少的步骤),要看这个问题到底是不是具有贪心选择性的。也就是看是不是全局最优解是由局部最优解产生的。对于这个事情,需要严格的数学证明才行。

http://www.zhihu.com/question/27883948 在知乎上问了这个问题。

 

posted @ 2015-03-31 21:10  雨尘之林  阅读(6116)  评论(0编辑  收藏  举报