一、求解最大子段和问题
(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)算法分析