算法--分治算法

分治算法

分治策略:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小),则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设汁策略叫做分治法。
可使用分治法求解的一些经典问题
(1)二分搜索
(2)大整数乘法
(3)Strassen矩阵乘法
(4)棋盘覆盖
(5)合并排序
(6)快速非序
(7)线性时间选择
(8)最接近点对问题
(9)循环赛日程表
(10)汉诺塔

分治算法–最大子数组问题

在这里插入图片描述

暴力求解

internal class Program
{
    static void Main(string[] args)
    {
        int[] priceArray = { 100, 113, 110, 85, 105, 102, 86, 63, 81, 101, 94, 106, 101, 79, 94, 90, 97 };
         //价格波动的数组
        int[] priceFluctuationArray = new int[priceArray.Length - 1];

        for(int i = 1;i < priceArray.Length;i++)
        {
            
            priceFluctuationArray[i-1] = priceArray[i] - priceArray[i-1];
        }


        int total = priceFluctuationArray[0];//默认数组的第一个元素是最大子数组
        int minIndex = 0;
        int maxIndex = 0;
        for(int i = 0;i < priceFluctuationArray.Length; i++)
        {
            //取得以i为子数组起点的所有子数组
            for(int j = i; j < priceFluctuationArray.Length; j++)
            {
                //先确定i后,确定j,求i到j的数组和,用for循环求和
                int totalTemp = 0; //临时 最大的子数组和
                for(int index = i; index < j + 1; index++)
                {
                    totalTemp += priceFluctuationArray[index];
                }
                if(totalTemp > total)
                {
                    total = totalTemp;
                    minIndex = i;
                    maxIndex = j;
                }
            }

        }
        Console.WriteLine("在第"+minIndex+"买入");
        Console.WriteLine("在第"+maxIndex+1+"卖出");
    }
}

分治求解

拆解问题

在这里插入图片描述

  1. 求数组【low,high】最大子数组问题
  2. 取mid中间值,把区间划分为【low,mid】【mid + 1,high】
  3. i , j是最大子数组的开始索引和结束索引,那i,j位置只有三种情况
    • i,j同时位于低区间
    • i,j同时位于高区间
    • i 位于低区间,j 位于高区间
  4. 得到三种情况的最大子数组,然后再比较哪个情况子数组最大
  5. 问题就转化为了先求【low,mid】的最大子数组,这个问题就和原问题是类似的,大问题变成了小问题
  6. 同理,求【mid + 1,high】也是一样
  7. 第三种情况可以直接使用for循环解决,因为已经确定mid,所以先求【i,mid】的最大数组,遍历一遍就可以确定 i 的位置,同理,求【mid + 1,j 】的最大数组,因为已经确定一端mid + 1, 只需要遍历一遍就可以确定 j 的位置,最后,两个最大数组相加就是最大子数组
internal class Program
{
    //最大子数组的结构体
    struct SubArray
    {
        public int startIndex;
        public int endIndex;
        public int total;//和
    }
    static void Main(string[] args)
    {
        int[] priceArray = { 100, 113, 110, 85, 105, 102, 86, 63, 81, 101, 94, 106, 101, 79, 94, 90, 97 };

        //价格波动的数组,求该数组的最大子数组
        int[] priceFluctuationArray = new int[priceArray.Length - 1];

        for (int i = 1; i < priceArray.Length; i++)
        {
            
            priceFluctuationArray[i - 1] = priceArray[i] - priceArray[i - 1];
        }

        SubArray subArray =  GetMaxSubArray(0, priceFluctuationArray.Length - 1, priceFluctuationArray);

        Console.WriteLine(subArray.startIndex);
        Console.WriteLine(subArray.endIndex);
        Console.WriteLine( "我们在第"+subArray.startIndex+"天买入"+",第"+(subArray.endIndex+1)+"天卖出");
    }

    static SubArray GetMaxSubArray(int low, int high, int[] array)//取得array数组,从low到high的最大子数组,返回结构体(开始索引、结束索引、子数组的和)
    {
        //终止条件,求一个值的最大子数组
        if (low == high)
        {
            SubArray subArray0;
            subArray0.startIndex = low;
            subArray0.endIndex = high;
            subArray0.total = array[low];
            return subArray0;
        }
        int mid = (low + high) / 2; //低区间[low,mid] 高区间[mid=1,high]
        //1. 低区间的最大子数组
        SubArray subArray1 = GetMaxSubArray(low, mid, array);
        //2. 高区间的最大子数组
        SubArray subArray2 = GetMaxSubArray(mid+1, high, array);

        //3. 从[low,mid]找到最大子数组[i,mid]

        int total1 = array[mid];
        int startIndex = mid;

        //临时存放最大子数组的和
        int totalTemp = 0;
        //从从mid到low找最大数组,已经确定一端为mid,往左遍历
        for(int i = mid; i >= low; i--)
        {
            
            totalTemp += array[i];
            //添加之后的和大于原本的和就更新最大子数组
            if (totalTemp > total1)
            {
                
                total1 = totalTemp;
                startIndex = i;
            }
        }

        //从[mid+1,high]找到最大子数组[mid+1,j]
        int total2 = array[mid+1];
        int endIndex = mid + 1;
        totalTemp = 0;
        for(int j = mid + 1; j <= high; j++)
        {
            totalTemp += array[j];
            if(totalTemp > total2)
            {
                total2 = totalTemp;
                endIndex = j;
            }
        }

        SubArray subArray3;
        subArray3.startIndex = startIndex;
        subArray3.endIndex = endIndex;
        subArray3.total = total1 + total2;
        
        //判断三种情况,哪一个最大
        if(subArray1.total>=subArray2.total && subArray1.total >= subArray3.total)
        {
            return subArray1;
        }
        else if (subArray2.total >= subArray1.total && subArray2.total >= subArray3.total)
        {
            return subArray2;
        }
        else
        {
            return subArray3;
        }
    }
}
  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
循环赛日程表问题是一个经典的组合数学问题,它的目标是设计一个比赛日程表,使得n个队伍两两之间都要比赛一次,且每个队伍每天只能进行一次比赛。这个问题可以使用分治算法解决。 下面是循环赛日程表分治算法的C语言代码实现: ```c #include<stdio.h> void Schedule(int **a, int n, int k) { if (n == 2) { a = 1; a = 2; a = 2; a = 1; } else { Schedule(a, n / 2, k); int i, j; for (i = 1; i <= n / 2; i++) { for (j = k + 1; j <= k + n / 2; j++) { a[i][j] = a[i + n / 2][j - n / 2]; } } for (i = n / 2 + 1; i <= n; i++) { for (j = k + 1; j <= k + n / 2; j++) { a[i][j] = a[i - n / 2][j - n / 2]; } } for (i = 1; i <= n / 2; i++) { for (j = k + n / 2 + 1; j <= k + n; j++) { a[i][j] = a[i + n / 2][j - n / 2 - 1] + n / 2; } } for (i = n / 2 + 1; i <= n; i++) { for (j = k + n / 2 + 1; j <= k + n; j++) { a[i][j] = a[i - n / 2][j - n / 2 - 1] + n / 2; } } Schedule(a, n / 2, k + n / 2); } } int main() { int n, i, j; printf("请输入参赛队伍数量:"); scanf("%d", &n); int **a = (int **) malloc((n + 1) * sizeof(int *)); for (i = 1; i <= n; i++) { a[i] = (int *) malloc((n + 1) * sizeof(int)); } Schedule(a, n, 0); printf("比赛日程表如下:\n"); for (i = 1; i <= n; i++) { for (j = 1; j <= n; j++) { printf("%d ", a[i][j]); } printf("\n"); } return 0; } ``` 该算法的思路是先把问题规模缩小到n/2,然后将n/2个队伍分成两组,第一组与第二组之间的比赛日程表可以由上一步得到,第一组与第三组之间、第二组与第四组之间的比赛日程表可以通过对上一步得到的日程表进行调整得到。然后再把问题规模缩小到n/4,重复上述过程,直到问题规模缩小到2。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值