zoukankan      html  css  js  c++  java
  • 分治法

        任何一个可以用计算机求解的问题所需的时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算;当n=2时,只要做依次排序即可;而当n较大时,问题就不那么容易处理了。

    “分治”(Divide and conque)就是分而治之,即讲一个难以直接解决的大问题。分割层一些规模较小的相同问题,以便各个击破,分而治之。基本步骤如下:

    1)分解:将原问题分解成若干个规模较小、相互独立、与原问题性质想听的子问题;

    2)解决:若子问题规模较小而容易解决则直接解决,否则递归的解决;

    3)合并:将各个子问题的解合并为原问题的解。

    应用一:

    排队购票问题。

    问题描述:

    一场比赛开始前,售票工作正在进行。每张球票为50元,现有30个人排队等待购票,其中有20个人手持50元的钞票,另外10个人手持100元的钞票。假设开始售票时售票处没有零钱,求出这30个人排队购票,使售票处不会出现找不开钱的局面的不同排队方案。(拿同样面值钞票的人对换位置后为同一种派排队方案)。

    算法分析:一般清醒,假设有m+n个人在排队购票,其中m个人手里有50元钞票,n个人手里拿100元钞票。求排队方案;

    分三种情况来考虑问题:

    1)n=0. 由于拿相同票值的人调换顺序后仍为同一种排队方案,所以f(m,0) = 1.

    2)m < n. 在这种情况下,无论怎么样排队,售票处都会面临找不开钱的局面,因此f(m,n) = 0.

    3)其他情况. 对于第 i+j 个人来说,他前面有 i+j-1 个人,对于他自己有两种情况。如果第 i+j 个人拿50元的钞票,则他前面的人有 i-1 个人拿50元钞票, j 个人拿100元钞票;如果第 i+j 个人拿100元钞票,则他前面有 i 个人拿50元钞票, j-1 个人拿100元钞票。因此,f(i, j) = f(i-1, j) + f(i, j-1).

    因此,根据以上分析,我们得到的解决方案代码为:

    public int solution(int m, int n) {
           if(n == 0) return 1;
           if(m < n) return 0;
           return solution(m-1, n) + solution(m, n-1);
    }
    View Code

    使用递推法,也就是动态规划,用数组存储之前计算的值,然后后面直接使用即可。

    public int solution(int m, int n) {
            int[][] f = new int[m+1][n+1];
            //设置n == 0的情况为1
            for(int i=1; i<=m; i++) 
                f[i][0] = 1;
            //设置m < n的情况为0
            for(int i=0; i<=m; i++) {
                for(int j=i+1; j<=n; j++)
                    f[i][j] = 0;
            }
            //设置其他情况
            for(int j=1; j<=n; j++) {
                for(int i=j; i<=m; i++) 
                    f[i][j] = f[i-1][j] + f[i][j-1];
            }
            return f[m][n];
        }
    View Code

    应用二:

    “放苹果”问题。把M个同样的苹果放在N个同样的盘子里,允许有的盘子空着不放,共有多少种不同的放法?注意:5,1,1和1,5,1是同一种放法。

    算法分析:因为允许有的盘子空着,因此我们可以根据盘子中是否有苹果分情况讨论。分为至少有一个盘子空着和所有的盘子中都有苹果两种情况。

    1)至少有一个盘子空着。即求m个苹果往n-1个盘子中放,有几种放法。

    2)所有的盘子中都有苹果。即每个盘中至少有一个苹果,则问题变为m-n个苹果放到n个盘子中,有几种放法。

    故对于一般情况来说,f(m, n) = f(m, n-1) + f(m-n, n);

    但是并不是所有的m,n都符合上面的地推式,因此我们可以根据m和n之间的关系讨论:

    1)m>n时,上面两种情况都满足,可以直接使用;

    2)m<n时,则即使每个盘子中只放一个苹果,也会有n-m个盘子空着,去掉这n-m个盘子,则剩余的问题变为,m个苹果放到m个盘子中有多少种排放方式。因此f(m, n) = f(m, m).

    出口:

    当n=1,即只有一个盘子时,则只能把所有苹果都放到这一个盘子里,因此f(m, 1) = 1;

    当m=0时,表示没有苹果可放,则f(0, n) = 1. 

    (对于递推式来说,n-1这样递减,因此一定会到达1,;而当n>m时,有f(n, m) = f(m, m),而m的变化为n-m,因此会到达0,因此递推式一定会到达出口)。

    代码为:

        public int putApple(int m, int n) {
            if(m == 0) return 1;
            if(n == 1) return 1;
            if(n > m) return putApple(m, m);
            return putApple(m, n-1) + putApple(m-n, n);
        }
    View Code

    应用三:

    红与黑。有一间长方形的房子,地上铺满了红色和黑色两种颜色的正方形瓷砖。当站在其中一块黑色的瓷砖上面时,只能向上下左右相邻的黑色瓷砖移动。请写一个程序,计算总共能够到达多少块黑色的瓷砖。

    算法分析:这个问题也可以描述成:给定一点,计算他所在的连通区域的面积。对于一般情况,设f(x, y)为从点(x, y)出发能够走过的黑瓷砖总数,则

    f(x, y) = 1 + f(x-1, y) + f(x+1, y) + f(x, y-1) + f(x, y+1).

    伪代码:

    int findNum(int x, int y) {
            if(x和y超过了矩阵范围)
                return 0;
            if(遇到红色瓷砖)
                return 0;
            将走过的瓷砖标记为红色;
            return 1 + findNum(x+1, y) + findNum(x-1, y) + findNum(x, y+1) + findNum(x, y-1);
        }
    View Code
  • 相关阅读:
    [转]汇编语言的准备知识给初次接触汇编者 4
    Javascript实现页面跳转的几种方式收藏
    [转]汇编语言的准备知识给初次接触汇编者 1
    jQuery常用的函数的简单描述 便于查阅
    解决win7光驱驱动找不到的问题
    tar
    liunx64运行飞信的问题
    centos6禁用ipv6
    仍然是yum问题rhel6使用centos的yum源
    【MyBatis】使用MyBatis的分页组件PageHelper时,多表关联下使用别名查询时,前台传参过来,根据参数排序的解决方案
  • 原文地址:https://www.cnblogs.com/little-YTMM/p/5367088.html
Copyright © 2011-2022 走看看