分治法

    任何一个可以用计算机求解的问题所需的时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于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

 

posted @ 2016-04-08 10:50  江湖小妞  阅读(493)  评论(0编辑  收藏  举报