基于分治法求解循环日程表问题

 实验内容:

        本实验要求基于算法设计与分析的一般过程(即带求解问题的描述、算法设计、算法描述、算法正确性证明、算法分析、算法实现与测试)利用分治法的循环日程表算法及挑战n≠2^{k}的情形(当然我这里就不挑战了),以及分治法的其他经典应用实例如快速排序、归并排序、棋盘覆盖问题等。

实验目的:

        理解分治法的核心思想以及分治法的求解过程。

实验步骤:

 步骤1:理解问题给出问题的描述

        设有 n=2^k 个运动员要进行网球循环赛。现要设计一个满足以下要求的比赛日程表:

        (1) 每个选手必须与其他 n−1 个选手各赛一次;

        (2) 每个选手一天只能参赛一次;

        (3) 循环赛在 n−1 天内结束。 按此要求将比赛日程表设计成有 n 行和 n-1 列的一个表。

        在表中的第 i 行,第 j 列处填入第 i 个选手在第 j 天所遇到的选手。其中 1≤i≤n,1≤j≤n−1。

步骤2:算法设计,包括算法策略与数据结构的选择

        按分治策略,可以将所有的选手分为两半,则 n 个选手的比赛日程表可以通过 n/2 个选手的 比赛日程表来决定。将左上角小块中的所有数字按其相对位置抄到右下角,又将左下角小块 中的所有数字按其相对位置抄到右上角,这样就分别安排好了选手 1 至选手 4 和选手 5 至选 手 8 在后 4 天的比赛日程。

步骤3:描述算法。采用源代码以外的形式来描述:(此处使用伪代码)

ROUND-ROBIN-CALENDAR(K,A)

         m=1

        n=2^k

        for i in 1 to n:

                A[1][i]=i

                C=1 //位置计数

                for s in 1 to k:

                        n/=2 

                        for t in 1 to n:

                                for i in m+1 to 2*m:

                                         for j in m+1 to 2*m:

                                                a[i][j+(t-1)*m*2]=a[i-m][j+(t-1)*m*2-m]

                                                a[i][j+(t-1)*m*2-m]=a[i-m][j+(t-1)*m*2]

                                                m*=2

步骤 4:算法正确性证明。需要这个环节,在理解的基础上对算法的正确性给予证明;

        分治算法求解主要遵循三大步骤,分解、解决、合并

        分解:将原问题划分成形式相同的子问题,规模可以不等,对半或 2/3 对 1/3 的 划分。

        解决:对于子问题的解决,很明显,采用的是递归求解的方式,如果子问题足够 小了,就停止递归,直接求解。

        合并:将子问题的解合并成原问题的解。 通过日程表找到的规律,我们通过迭代的方式进行实现

步骤 5:算法复杂性分析,包括时间复杂性和空间复杂性;

 

        步骤 6:算法实现与测试。

#include <iostream>
using namespace std;

void table(int k,int**a) {
    int n=2;
    a[0][0]=1; a[0][1]=2;
    a[1][0]=2; a[1][1]=1;
    for(int t=1; t<k; t++) {
        int temp=n;
        n*=2;

        for(int i=temp; i<n; i++) // 填写左下角元素
            for(int j=0; j<temp; j++)
                a[i][j]=a[i-temp][j]+temp; // 左下角元素和左上角元素的对应关系
        for(int i=0; i<temp; i++) // 填写右上角元素
            for(int j=temp; j<n; j++)
                a[i][j]=a[i+temp][j-temp]; // 右上角元素和左下角元素的对应关系
        for(int i=temp; i<n; i++) // 填写右下角元素
            for(int j=temp; j<n; j++)
                a[i][j]=a[i-temp][j-temp]; // 右下角元素和左上角元素的对应关系
    }
}

int hanglie(int k) {
    int n=1;
    for (int i=1; i<=k; i++) {
        n*=2;
    }
    return n;
}

int main() {
    int k;
    cin>>k;
    int n=hanglie(k);
    int** arr=new int* [n];
    for(int i=0; i<n; i++) {
        arr[i]=new int[n];
        for(int j=0; j<n; j++) {
            arr[i][j]=0;
        }
    }
    table(k, arr);
    for(int i=0; i<n; i++) {
        for(int j=0; j<n; j++) {
            cout<<arr[i][j]<< " ";
        }
        cout << endl;
    }
    system("pause");
    return 0;
}

实验总结

        分治法的设计思想是:将一个难以直接解决的大问题,分割成一些规模较小的相 同问题,分而治之。

        分治策略是:对于一个规模为 n 的问题,若该问题可以容易地解决(比如说规模 n 较小)则直接解决,否则将其分解为 k 个规模较小的子问题,这些子问题互相 独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到 原问题的解。这种算法设计策略叫做分治法。

分治法所能解决的问题一般具有以下几个特征:

         1.该问题的规模缩小到一定的程度就可以容易地解决

         2.该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。

         3.利用该问题分解出的子问题的解可以合并为该问题的解;

         4.该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子 问题。 分治法在每一层递归上都有三个步骤:

             1.分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

             2.解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题

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

         总结: 分治算法是一种思想,将问题分而治之,通常需要借助递归的帮助。关键点 在于如何将问题分解,不仅仅将问题规模减半就可以,还需要仔细思考如何将分 解的问题进行合并。需要注意分治算法分解的时候,出来的自问题通常是与原问 题性质相同,但规模小的子问题

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值