分治法2

来源:互联网 发布:英雄传奇挂机软件 编辑:程序博客网 时间:2024/06/14 17:18

Chess-Board

问题描述:有2kX2k大小的棋盘,其中有一块和其他的不一样。使用4种L型的card将棋盘铺满棋盘。如下图所示:
这里写图片描述
问题分析:首先所给的4个card是可以将最小矩形的三个块组合的所有情况都考虑到了(C34=4),所以我们将问题规模减小为(2X2),实际上就是1时,问题都是可以解决的。问题的难点就在于如何将问题分解成多个类似的小问题,很自然将上面的大图减半,但是生成的4个小图的形式不一样,为了保证一样,我们只需要在他们的聚合点(也就中心点附近添加一个合适的card)如下图所示,就可以将问题分解成相似的小问题了。
这里写图片描述
代码实现:

# Import a library of functions called 'pygame'import pygameimport time# Define the colors we will use in RGB formatBLACK = (0, 0, 0)WHITE = (255, 255, 255)BLUE = (0, 0, 255)GREEN = (0, 255, 0)RED = (255, 0, 0)YELLOW = (255, 255, 0)Colors = [RED, BLUE, GREEN, YELLOW]# define a global queue named BoardsBoards = []def Card(tag, left_upper):    """    tag:card的类型,为0就代表第一个方块为空,同理类推    left_upper:card所在矩形左上角的坐标    """    global Boards    blocks = []    nums = [0, 1, 2, 3]    nums.remove(tag)    for i in nums:        block = []        row = int(i / 2)        col = i % 2        block.append((left_upper[1] + col) * 40 + 10)        block.append((left_upper[0] + row)*40 + 10)        block.append(40)        block.append(40)        blocks.append(block)    blocks.append(Colors[tag])    Boards.append(blocks)def DrawBoard(dr, dc, size):    pygame.init()    window_size = [size * 40 + 20, size * 40 + 20]    # Set the height and width of the screen    screen = pygame.display.set_mode(window_size)    pygame.display.set_caption("chess board")    done = False    clock = pygame.time.Clock()    while not done:        # This limits the while loop to a max of 10 times per second.        # Leave this out and we will use all CPU we can.        clock.tick(10)        for event in pygame.event.get():  # User did something            if event.type == pygame.QUIT:  # If user clicked close                done = True        # 画出相应的grid        screen.fill(WHITE)        for i in range(size + 1):            pygame.draw.line(screen, BLACK, [i * 40 + 10, 10], [i * 40 + 10, size * 40 + 10], 3)            pygame.draw.line(screen, BLACK, [10, i * 40 + 10], [size * 40 + 10, i * 40 + 10], 3)        # Draw a rectangle outline        pygame.draw.rect(screen, RED, [dc * 40 + 10,dr * 40 + 10, 40, 40], 0)        for L in Boards:            pygame.draw.rect(screen, L[3], L[0], 0)            pygame.draw.rect(screen, L[3], L[1], 0)            pygame.draw.rect(screen, L[3], L[2], 0)            time.sleep(2)            pygame.display.flip()        pygame.display.flip()        # Be IDLE friendly       # pygame.quit()def chBoard(tr, tc, dr, dc, size):    """    坐标都是从0开始的    tr:表示要处理的子方格的左上角横坐标    tc:表示要处理的子方格的左上角竖坐标    dr:表示特殊方块的row    dr:表示特殊方块的col    size:表示问题的规模(2^k)    """    global tile    tile += 1    s = size / 2    # 判断特殊方块落在哪个区域    num_row = 0 if ((dr - tr) < s) else 1    num_col = 0 if ((dc - tc) < s) else 1    k = num_row * 2 + num_col    # 填补中心点的另外三个方块    Card(k, [tr + s - 1, tc + s - 1])    if (s == 1):        return    # 分别对四个区域进行处理    # 左上角覆盖    if (k == 0):        chBoard(tr, tc, dr, dc, s)    else:        chBoard(tr, tc, tr + s - 1, tc + s - 1, s)    # 右上角覆盖    if (k == 1):        chBoard(tr, tc + s, dr, dc, s)    else:        chBoard(tr, tc + s, tr + s - 1, tc + s, s)    # 左下角覆盖    if (k == 2):        chBoard(tr + s, tc, dr, dc, s)    else:        chBoard(tr + s, tc, tr + s, tc + s - 1, s)    # 右下角覆盖    if (k == 3):        chBoard(tr + s, tc + s, dr, dc, s)    else:        chBoard(tr + s, tc + s, tr + s, tc + s, s)if __name__ == "__main__":    tile = 0    chBoard(0, 0, 3, 5, 16)    print(Boards)    DrawBoard(3,5,16)

从上面的代码可以看出:
这里将一个大问题分解成了四个小问题,但是四个小问题,由于特殊块是否在其中,又呈现出两种不同的形态(区别在于特殊块);
通过可视化,我们可以发现在填充时也是一个区域内,不断递归式的进行填充。
这个问题的复杂度为O(4n)

找到第K个最小数

问题:在一组无序的数中,找到第K个最小数,同时要在O(n)的时间复杂度内。
解决方法:借用快速排序的思想,每次我们确定一个轴点,如果这个轴点是第k个,就返回其值;否则不是就转到相应的子区间内,寻找。这样问题的复杂度就可以降为O(n)
通过进一步分析,可以发现最坏的情况复杂度为O(n2),比如,我们在一个从大到小的顺序里,找最小。
出现上面情况的主要原因在轴点划分的十分不均匀,所以我们可以采取下列措施使得轴点划分的十分均匀:
1. 将所有的元素划分成5个一组,一共n/5
2. 对每个组进行排序
3. 抽取每个组的中间元素,组成一个新的向量,一共n/5.
4. 返回第一步进行循环,直到size为1。
5. 返回这个x。这个x就是partition的x。
整体的代码如下所示:

#include<iostream>#include<vector>using namespace std;void PopSort(vector<int> &A ,int start,int end){    for (int j = start; j < end; j++){        for (int i = 0; i < end-j-1; i++){            if (A[i + start] > A[i + start + 1]){                int temp = A[i + start];                A[i + start] = A[i + start + 1];                A[i + start + 1] = temp;            }        }    }}int Partition(vector<int> &A, int start, int end){    int axis_point = A[start];    while (start < end){        while (A[end] >= axis_point && start < end)            end--;        A[start] = A[end];        while (A[start] <= axis_point && start < end)            start++;        A[end] = A[start];    }    A[start] = axis_point;    return start;}//这里求的是第k个最大的,但是vector的index是从0开始的int Findkelement(vector<int> &A, int start, int end, int k){    if (start == end) return A[start];    int prvo = Partition(A, start, end);    int j = prvo - start + 1;    if (k == j) return A[prvo];    if (k < j) return Findkelement(A, start, prvo-1, k); //如果这里不减1,当prvo等于1,k=1的时候,就进入了死循环。    else return Findkelement(A, prvo + 1, end, k - j);}//改良后的代码int Select(vector<int> &A, int k){    //元素少于75的没有必要用复杂的复杂的方法。    if (A.size() < 75) return Findkelement(A, 0, A.size() - 1, k);    //分组并进行排序    int groups = (A.size() + 4) / 5;    for (int i = 0; i < groups-1; i++){        PopSort(A, i * 5, (i + 1) * 5);    }    PopSort(A, (groups - 1) * 5, A.size());    vector<int> Median;    for (int j = 0; j < groups-1; j++){        Median.push_back(A[3 + j * 5]);    }    Median.push_back(A[(groups - 1) * 5]);    //选出median的中位数    int x = Select(Median, (Median.size()+1) / 2);    //根据中位数对元素进行分组    vector<int> S1, S2;    for (int i = 0; i < A.size(); i++){        if (A[i] <= x) S1.push_back(A[i]);        else S2.push_back(A[i]);    }    int value=0;    //在对应的分组中继续找对应的元素    if (k <= S1.size()) return Select(S1, k);    else return Select(S2, k - S1.size());//这里必须要添加return否则就会有逻辑错误。}void main(){    vector<int> A = { 3, 4, 5, 6, 7, 8,3,4,5,6,7,8,43,3,4,5,6,7,8,3,2,7,4,3,45,5,6,4,3,5,6,5,4,3,5,6,7,8,65,5,3,3,5,6,7,7,45,4,4,3,5,6,4,3,54,6,4,6,4,3,6,7,6,5,4,23,34,5,6,6,7,5,5,4,4,3,4,5,6,5,4,3,3,4,5,6,4,4,5,6,78,8,6,5,54,4,5,6,6,4,4,3, 92, 4, 6, 8, 0, 4, 2 };    cout << Select(A,109) << endl;}

对上面的算法进行复杂度分析
1. 对于M这个数组的中位数x,至少有n5/10数小于它
2. 而小于的每个数在A中至少大于等于3个数,所以在A中只有3n5/10小于它,同理也至少有这么多数大于它。所以x取在了中间。
3. 当n>75,3n5/10n/4;所以我们的时间复杂度公式可以表示为T(n)cn+T(n/5)+T(3n/4),又因为n/5+3n/4=19n/20=εn0<ε<1,所以这个最差时间复杂度为O(n);
4. 所以从3中我们可以看出为什么是5的原因,在这里5,75是相互对应的。

原创粉丝点击