递归与分治策略描述的思想是将一个难以直接解决的大问题分割成一些规模较小的相同问题,以便各个击破,分而治之。以下简要介绍几种运用该方法解决的著名问题,以备学习复习笔试面试之用。这些内容包括:
1.汉诺塔问题
2.棋盘覆盖
3.循环赛日程表
4.二分搜索
5.快速排序
6.归并排序
下面逐一描述:
1.汉诺塔问题
问题描述:有三根杆子A,B,C。A杆上有N个(N>1)穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至C杆:
每次只能移动一个圆盘;大盘不能叠在小盘上面。提示:可将圆盘临时置于B杆,也可将从A杆移出的圆盘重新移回A杆,但都必须遵循上述两条规则。
问:如何移?最少要移动多少次?
解法:
#include <iostream>
using namespace std;
int count=0;
//移动杆的显式表示
void Move(int i,char a,char c)
{
cout<<"第"<<++count<<"次:将"<<i<<"号盘子从"<<a<<"移动至"<<c<<endl;
}
//在该函数中,n表示盘子的个数(按起始杆由上到下依次编号1...n)
//a表示起始杆,b表示中转杆,c表示目的杆
void Hanoi(int n,char a,char b,char c)
{
if (n==1)
Move(1,a,c);
else
{
Hanoi(n-1,a,c,b);//将上面的n-1个盘子从a移动到b,c作为辅助
Move(n,a,c);//将第n个盘子由a移动到c
Hanoi(n-1,b,a,c);//将剩下的n-1个盘子从b移动到c上,a作为辅助
}
}
int main()
{
Hanoi(4,'A','B','C');
return 0;
}
结果截图:
2.棋盘覆盖
问题描述:在一个 2^k * 2^k(k>=1) 个方格组成的棋盘中,若恰有一个方格与其它方格不同,则称该方格为一特殊方格,称该棋盘为一特殊棋盘。显然特殊方格在棋盘上出现的位置有 4^k 种情形。因而对任何 k>=0 ,有 4^k 种不同的特殊棋盘。下图所示的特殊棋盘为 k=2 时 16 个特殊棋盘中的一个。
在棋盘覆盖问题中,要用下图中 4 中不同形态的 L 型骨牌覆盖一个给定的特殊棋牌上除特殊方格以外的所有方格,且任何 2 个 L 型骨牌不得重叠覆盖。易知,在任何一个 2^k * 2^k 的棋盘中,用到的 L 型骨牌个数恰为 (4^k-1)/3 。
解法:
#include <iostream>
#define MAX_SIZE 8
using namespace std;
int tile = 0;//表示L型骨牌编号
int Brand[MAX_SIZE][MAX_SIZE];//表示整个棋盘
//分治法思想-棋盘覆盖
//tr表示左上角行数,tc表示左上角列数,dr,dc表示特殊行特殊列,size表示规格
void ChessBoard(int tr,int tc,int dr,int dc,int size)
{
if (size==1) return;
int t = tile++;
int s = size/2;
//1.覆盖左上角棋盘
if (dr<tr+s && dc<tc+s)//特殊方格在这个棋盘中
ChessBoard(tr,tc,dr,dc,s);
else//此棋盘中无特殊方格,将特殊方格放在右下角
{
Brand[tr+s-1][tc+s-1]=t;
ChessBoard(tr,tc,tr+s-1,tc+s-1,s);//覆盖其余
}
//2.覆盖右上角棋盘
if(dr<tr+s && dc>=tc+s)//特殊方格在这个棋盘中
ChessBoard(tr,tc+s,dr,dc,s);
else//特殊方格不在该棋盘中,将特殊方格填充在左下角
{
Brand[tr+s-1][tc+s]=t;
ChessBoard(tr,tc+s,tr+s-1,tc+s,s);//覆盖其余
}
//3.覆盖左下角棋盘
if (dr>=tr+s && dc<tc+s)//特殊方格在这个棋盘中
ChessBoard(tr+s,tc,dr,dc,s);
else//特殊方格不在该棋盘中,将特殊方格填充在右上角
{
Brand[tr+s][tc+s-1]=t;
ChessBoard(tr+s,tc,tr+s,tc+s-1,s);//覆盖其余
}
//4.覆盖右下角棋盘
if (dr>=tr+s && dc>=tc+s)//特殊方格在这个棋盘中
ChessBoard(tr+s,tc+s,dr,dc,s);
else//特殊方格不在该棋盘中,将特殊方格填充在左上角
{
Brand[tr+s][tc+s]=t;
ChessBoard(tr+s,tc+s,tr+s,tc+s,s);//覆盖其余
}
}
int main()
{
ChessBoard(0,0,0,3,MAX_SIZE);
for (int i=0;i<MAX_SIZE;i++)
{
for (int j=0;j<MAX_SIZE;j++)
cout<<Brand[i][j]<<'\t';
cout<<endl;
}
return 0;
}
结果截图:
3.循环赛日程表
问题描述:设有n=2^k(k>=1)个选手参加网球循环赛,循环赛共进行n-1天,每位选手要与其他n-1位选手比赛一场,且每位选手每天必须比赛一场,不能轮空。试按此要求为比赛安排日程。
解法:
#include <iostream>
using namespace std;
#define N 64
void GameTable(int k,int a[][N])
{
//n=2^k(k>=1)个选手参加比赛,数组下标从1开始
int n=2;//k=1,可直接求出
//求解两个选手比赛日,得到左上角元素
a[1][1]=1;a[1][2]=2;
a[2][1]=2;a[2][2]=1;
int i,j,t;
for (t=1;t<k;t++)//迭代处理,依次处理2^2,...2^k个选手比赛日程
{
int temp=n;
n*=2;
//填左下角元素
for (i=temp+1;i<=n;i++)
for (j=1;j<=temp;j++)
//左下角元素和左上角元素的对应关系
a[i][j]=a[i-temp][j]+temp;
//将左下角元素抄到右上角
for (i=1;i<=temp;i++)
for (j=temp+1;j<=n;j++)
a[i][j]=a[i+temp][(j+temp)%n];
//将左上角元素抄到右下角
for (i=temp+1;i<=n;i++)
for (j=temp+1;j<=n;j++)
a[i][j]=a[i-temp][j-temp];
}
cout<<"运动员编号\t";
for (i=1;i<n;i++)
{
cout<<"第"<<i<<"天\t";
}
cout<<endl;
for (i=1;i<=n;i++)
{
cout<<i<<"号运动员:\t";
for (j=2;j<=n;j++)
cout<<a[i][j]<<'\t';
if (j==n)
cout<<n;
cout<<endl;
}
}
void main()
{
int a[N][N];
int k;
cout<<"输入选手个数的次数:";
cin>>k;
GameTable(k,a);
}
结果截图:
4.二分搜索
描述:将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
解法:
#include <iostream>
using namespace std;
//第一个参数表示有序数组,n表示数组元素个数,key表示需要寻找的数字
int BinarySearch(int a[],int n,int key)
{
int low=0,high=n-1;
while (low<=high)
{
int mid=(low+high)/2;
if (key==a[mid]) return mid;
if (key>a[mid]) low=mid+1;
else high=mid-1;
}
return -1;
}
int main()
{
int a[]={1,3,5,8,22,45,65,78,102};
cout<<BinarySearch(a,9,65)<<endl;
return 0;
}
结果截图:
5.快速排序
算法思想见图:
程序描述:
#include <iostream>
using namespace std;
//求出“基准”所在的位置
int patition(int arr[],int low,int high)
{
int key=arr[low],i=low,j=high;//key保存初始基准位置
if (low<high)
while (i<j)
{
while (i<j && arr[j]>=key) j--;//没有需要移动的数
if (i<j)//存在需要移动的数
{
arr[i]=arr[j];
i++;//i处数据已定,不需比较,故i++
}
while (i<j && arr[i]<=key) i++;
if (i<j)
{
arr[j]=arr[i];
j--;
}
}
arr[i]=key;//此时i==j
return i;
}
void QuickSort(int arr[],int low,int high)
{
if (low<high)
{
int mid=patition(arr,low,high);
QuickSort(arr,low,mid-1);
QuickSort(arr,mid+1,high);
}
}
int main()
{
int arr[]={2,3,4,1,3,45,23,12,43};
cout<<"排序前:";
for (int i=0;i<9;i++)
{
cout<<arr[i]<<'\t';
}
cout<<endl<<"排序后:";
QuickSort(arr,0,8);
for (i=0;i<9;i++)
{
cout<<arr[i]<<'\t';
}
cout<<endl;
return 0;
}
6.归并排序
思想描述图示:
程序描述:
#include <iostream>
using namespace std;
//归并排序示例程序--从小到大
void merge(int array[],int p,int q,int r)
{
int i,begin1,end1,begin2,end2;
//申请空间,空间大小为两个已排序列
int *temp = new int[r-p+1];
begin1=p;end1=q;begin2=q+1;end2=r;i=0;
while ((begin1<=end1)&&(begin2<=end2))
{//将有序的元素进行比较
if (array[begin1]<array[begin2])
temp[i++]=array[begin1++];
else
temp[i++]=array[begin2++];
}
while (begin1<=end1)//此时若前面的序列还有数字,全部附到temp的后面
temp[i++]=array[begin1++];
while (begin2<=end2)//此时若后面的序列还有数字,也全部附到temp的后面
temp[i++]=array[begin2++];
for (int k = 0;k<r-p+1;k++)
array[p+k]=temp[k];
delete []temp;
}
void merge_sort(int array[],int first,int last)
{
int mid=0;
if (first<last)
{
mid = (first+last)/2;
merge_sort(array,first,mid);
merge_sort(array,mid+1,last);
merge(array,first,mid,last);
}
}
int main()
{
int array[]={1,5,2,6,3};
for (int i=0;i<5;i++)
{
cout<<array[i]<<"\t";
}
cout<<endl;
merge_sort(array,0,5);
for (i=0;i<5;i++)
{
cout<<array[i]<<"\t";
}
cout<<endl;
return 0;
}
- 大小: 123.2 KB
- 大小: 5 KB
- 大小: 10.9 KB
- 大小: 4.6 KB
- 大小: 8.4 KB
- 大小: 1.5 KB
- 大小: 90.8 KB
- 大小: 13.1 KB
分享到:
相关推荐
在这个题目中,我们要探讨的是"最大字段和问题",这是一个经典的算法问题,通常用分治法(Divide and Conquer)来解决。下面将详细介绍这个问题及其解决方案。 **最大字段和问题**,也被称为**最大子序列和问题**,...
分治法是一种常见的算法设计策略,其核心思想是将一个复杂的问题分解成两个或更多的相同或相似的子问题,直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。在计算机科学中,分治法被广泛应用于排序...
分治法是一种将大规模问题分解成若干较小规模的相同或相似子问题,并通过递归方式解决这些子问题来求解原问题的方法。这种方法通常包括三个步骤:**分解**、**解决**、**合并**。 1. **分解**:将原问题分成多个...
总的来说,本项目旨在通过实例演示如何使用数组下标法和分治法求解众数问题,同时也提供了一种实践和分析不同算法效率的方式。对于学习算法和数据结构的初学者,这是一份极好的学习材料,可以帮助他们深入理解这两种...
这在解决线性方程组或图论问题中很常见,通过置换可以简化问题结构或揭示隐藏的规律。 五、经典问题应用 1. **斐波那契数列**:利用2×2矩阵的性质,可以将斐波那契数列的计算转化为矩阵的乘法,显著提高计算速度。...
分治法的核心是“分”、“治”、“合”。首先,将原问题分解成若干个规模较小、相互独立、与原问题形式相同的子问题;其次,递归地解决这些子问题;最后,将子问题的解合并得到原问题的解。例如,1015 - Jury ...
本压缩包包含了一些学校在ACM培训中使用的资料,旨在帮助参赛者理解和掌握三种常见的算法:分治、回溯和枚举。这些算法在解决复杂问题时起着至关重要的作用。 **分治策略**: 分治是一种将大问题分解为若干个相似的...
分治法有助于简化问题,提高算法效率。 5. **图**:图是一种数据结构,由顶点和边构成,用于表示对象之间的关系。图算法包括最短路径(Dijkstra、Floyd)、最小生成树(Prim、Kruskal)、拓扑排序等,广泛应用于...
5. 分治法:将大问题分解为小问题求解,然后合并结果,如快速排序、归并排序、汉诺塔问题等。 6. 贪心算法:每一步都采取局部最优解,以期望达到全局最优,如活动选择问题、霍夫曼编码等。 7. 图算法:如Dijkstra...
分治法利用了递归的思想,将问题分解为较小的子问题,然后将子问题的解合并以获得原问题的解。对于最大子数组和问题,分治法首先将数组分为左右两部分,然后分别求出左半部分、右半部分以及跨越中间点的子数组的最大...
实验报告“2014212065-沈嘉樑-实验四1”主要探讨了两个核心算法——分治法与贪心法在实际问题中的应用。以下是这两个算法的详细说明以及它们在实验内容中的应用。 **分治法(Divide and Conquer)** 分治法是一种...
本篇文章主要介绍了几种常见的算法设计方法,包括迭代法、穷举搜索法,以及在标签中提到的分治法、动态规划和贪心法。 1. **迭代法**: 迭代法是一种通过不断更新变量来逼近问题解的方法,常用于求解方程的近似根...
分治法是一种常见的算法设计策略,本章通过具体实例介绍了分治法的设计思想和应用场景。 **4.1-1至4.1-6**: - 这些题目可能要求学生分析分治法算法的时间复杂度或空间复杂度。 - 例如,4.1-1可能要求学生推导出...
常见的算法类型包括排序(冒泡、选择、插入、快速、归并、堆排序等)、搜索(深度优先搜索、广度优先搜索)、动态规划、贪心算法、回溯法、分治法等。对于每种算法,理解其基本思想、时间复杂度和空间复杂度是基础,...
在面对程序设计题目时,我们首先需要理解问题的需求,然后选择适合的算法策略,如分治法、动态规划、贪心算法或回溯法等。 数据结构的选择对于算法的效率至关重要。例如,数组和链表是最基本的数据结构,它们分别在...
2. **分治法**:分治法是将问题分成两半,分别解决,然后合并结果。对于最大子序列和问题,我们可以找到两个子数组的最大和,然后比较这两个子数组的和与它们各自的最大子数组之和的较大值。这种方法虽然直观,但...
这里给出的复习题目涉及了多种常见的算法策略,包括分治法、动态规划法、贪心法和回溯法。以下是这些算法的基本概念和相关知识点: 1. **分治策略**:分治法是将大问题分解为若干个相似的子问题,然后递归地解决子...
7. **分治法**:将大问题分解为小问题求解,如Strassen矩阵乘法、快速傅里叶变换等。 8. **数据结构**:包括链表、树(二叉树、平衡树、B树、红黑树等)、栈、队列、哈希表、堆、图等,它们是算法实现的基础。 在...
对于这些题目,理解和掌握基本的算法设计原则,如分治法、贪心策略、回溯法,以及熟悉常用的数据结构如链表、栈、队列、树、图,都是必不可少的。 在准备华为的机试时,你需要深入理解并熟练运用这些知识点。不仅要...
8. **分治法的应用**:0/1背包问题不适合用分治法解决,因为它不满足分治法的分解条件。 9. **分治策略**:循环赛日程表的实现通常利用分治策略,如二分查找或类似的分解方法。 10. **拉斯维加斯算法**:这种随机...