用分治法求解组合问题(最大子段和问题、棋盘覆盖问题)

一、求解最大子段和问题
(1)问题描述
在这里插入图片描述
(2)蛮力法求解
在这里插入图片描述

long maxSubSum1(int a[],int n)
{       int i,j,k;
  long maxSum=a[0],thisSum; 
  for (i=0;i<n;i++)     //两重循环穷举所有的连续子序列
  {  for (j=i;j<n;j++)
    {   thisSum=0;
      for (k=i;k<=j;k++)
        thisSum+=a[k];
      if (thisSum>maxSum)  //通过比较求最大连续子序列之和
        maxSum=thisSum;
    }
  }
  return maxSum;
}

在这里插入图片描述
(3)对蛮力法求解的改进
在这里插入图片描述

long maxSubSum2(int a[],int n)
{      int i,j;
  long maxSum=a[0],thisSum;
  for (i=0;i<n;i++)                 // i是子列左端位置
  {     thisSum=0;                // thisSum是从a[i]到a[j]的子列和
    for (j=i;j<n;j++)          // j是子列右端位置
    {      thisSum+=a[j];
       if (thisSum>maxSum)
        maxSum=thisSum;
    }
  }
  return maxSum;
}

T(n)=O(n2)
(4)分治法求解
1、基本思路在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 关键在于第三种问题的求解方案:
    中间向俩边找最大值,最后将俩个最大值加到一起

举例:
在这里插入图片描述
2.算法

long maxSubSum3(int a[]int left,int right) 
//求a[left..right]序列中最大子段和
{  int i,j;
   long maxLeftSum,maxRightSum;
   long maxLeftBorderSum,leftBorderSum;
   long maxRightBorderSum,rightBorderSum;
   if (left==right)  //子序列只有一个元素时
   {  if (a[left]>0)  //该元素大于0时返回它
      return a[left];
    else   //该元素小于或等于0时返回0
       return 0; 
   } 
   int mid=(left+right)/2;   //求中间位置
  maxLeftSum=maxSubSum3(a,left,mid); //求左边
  maxRightSum=maxSubSum3(a,mid+1,right); //求右边
  maxLeftBorderSum=0,leftBorderSum=0;
  for (i=mid;i>=left;i--)    //求出以左边加上a[mid]元素
  {  leftBorderSum+=a[i];    //构成的序列的最大和
        if (leftBorderSum>maxLeftBorderSum)
      maxLeftBorderSum=leftBorderSum;
  }
  maxRightBorderSum=0,rightBorderSum=0;
  for (j=mid+1;j<=right;j++)  //求出a[mid]右边元素
  {  rightBorderSum+=a[j];    //构成的序列的最大和
     if (rightBorderSum>maxRightBorderSum)
      maxRightBorderSum=rightBorderSum;
  }
  return max3(maxLeftSum,maxRightSum,
     maxLeftBorderSum+maxRightBorderSum); 
} 

3、算法分析
在这里插入图片描述
(5)
在这里插入图片描述

int maxSubSum4(int a[],int n)
{  int i,max=0,sum=0;
  for (i=0;i<n;i++)
  {  
      sum+=a[i];//向右累加
    if (sum<0) //若当前子序列和为负数,重新开始下一子序列
      sum=0;
    if (sum>max)//发现更大和则更新当前结果
      max=sum;
  }
  return max;
}

时间复杂度为O(n)

二、求解棋盘覆盖问题
在这里插入图片描述
(1)基本思路在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)算法
在这里插入图片描述

#include<stdio.h>
#define MAXSIZE 1025
//问题表示
int k;    //棋盘大小
int x,y;   //特殊方格的位置
//求解问题表示
int board[MAXSIZE][MAXSIZE];
int tile=1; 
void TileBoard(int tr,int tc,int dr,int dc,int size)
{   if(size==1) return;  //递归出口
    int t=tile++;   //取一个三格骨牌,其牌号为tile
    int s=size/2;   //分割棋盘
    //处理左上角象限的子棋盘
    if(dr<tr+s && dc<tc+s)  //特殊方格在此象限中
 TileBoard(tr,tc,dr,dc,s);
    else    //此象限中无特殊方格
    { board[tr+s-1][tc+s-1]=t; //用t号三格骨牌覆盖右下角
 TileBoard(tr,tc,tr+s-1,tc+s-1,s);//将右下角作为特殊方格继续处理该象限
    }
    //处理右上角象限的子棋盘
    if(dr<tr+s && dc>=tc+s)           //特殊方格在此象限中 
 TileBoard(tr,tc+s,dr,dc,s);  
    else    //此象限中无特殊方格
    { board[tr+s-1][tc+s]=t; //用t号三格骨牌覆盖左下角
 TileBoard(tr,tc+s,tr+s-1,tc+s,s); //将左下角作为特殊方格继续处理该象限
    }
 //处理左下角象限的子棋盘
    if(dr>=tr+s && dc<tc+s)  //特殊方格在此象限中
 TileBoard(tr+s,tc,dr,dc,s);  
    else    //此象限中无特殊方格
    {   board[tr+s][tc+s-1]=t;   //用t号三格骨牌覆盖右上角
        TileBoard(tr+s,tc,tr+s,tc+s-1,s);
    //将右上角作为特殊方格继续处理该象限
    }
    //处理右下角象限的子棋盘
    if(dr>=tr+s && dc>=tc+s)  //特殊方格在此象限中
        TileBoard(tr+s,tc+s,dr,dc,s); 
    else    //此象限中无特殊方格
    { board[tr+s][tc+s]=t;   //用t号三格骨牌覆盖左上角
 TileBoard(tr+s,tc+s,tr+s,tc+s,s);   
    //将左上角作为特殊方格继续处理该象限
    }
}
void main()
{       int k;  
        printf(“请输入k值,棋盘的大小为2的k次方:);  
        scanf(%d”,&k); 
        int size = 1<<k;
        int x,y;   //(x,y)为特殊方格的下标  
        scanf(%d%d”,&x,&y);  
        Board[x][y]=0;            //初始特殊方格标号为0   
        TileBoard(0,0,x,y,size);
        for(int i=0;i < size ; i++)  
        {   for(int j=0;j < size ; j++)  
                printf(%4d” ,board[i][j]);     
            printf(“\n”);  
        }  
} 

在这里插入图片描述
(3)算法分析
在这里插入图片描述

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值