分治法(1)旋转矩阵,拿金币,棋盘覆盖
分治法:
参考:
(36条消息) 分治算法详细讲解(含经典例题分析)_nan_black的博客-CSDN博客_分治算法几个经典例子
(36条消息) 经典算法(1)分治法_胡乱huluan的博客-CSDN博客
(1);把a问题分成若干个小问题
(2):保证每一个小问题的解法都相同,并保证这些小问题能整合为a问题的解
(3):用递归或者循环来解每一个小问题
(4):把所有小问题的解整合为a问题的解
1.旋转矩阵:
我们可以通过电脑构造一个螺旋方阵
例如,一个边长为5的螺旋方阵样子如下
1 16 15 14 13
2 17 24 23 12
3 18 25 22 11
4 19 20 21 10
5 6 7 8 9
思路:
把这个方阵分成多层,每层的阶数为(s)4个部分(蓝,红,绿,灰)
每个部分都是s-1个数,求解每一部分只需要改变行和列
然后用相同的方法求里层(s-2)
最后把所有的分解整合为总问题的解
#include<bits/stdc++.h> using namespace std; int a[1000][1000]; int size; void fz() { int s=size; int i=0,j=0,num=1; int b; for(;s!=0;s-=2)//结束标志为方阵的阶数为1或零 { if(s==1)//阶数为1时,直接把num放进方阵中并跳出 { a[i][j]=num; break; } for(b=0;b<s-1;b++,i++)//用一个变量b来控制数量 { a[i][j]=num; num++; } for(b=0;b<s-1;b++,j++) { a[i][j]=num; num++; } for(b=0;b<s-1;b++,i--) { a[i][j]=num; num++; } for(b=0;b<s-1;b++,j--) { a[i][j]=num; num++; } i++; j++; } for(int i=0;i<=size-1;i++) { for(int j=0;j<=size-1;j++) { printf("%5d",a[i][j]); } cout<<endl; } } int main() { cin>>size; fz(); return 0; }
2.典型二分法:
问题:金块问题
老板有一袋金块(共n块),最优秀的雇员得到其中最重的一块,最差的雇员得到其中最轻的一块,假设有一台比较重量的仪器,我们希望用最少的比较次数找出最重的金块
方法:二分递归
思路
- 三种情况:数量为1,数量为2,数量大于2
- 第二种情况直接用max找两数最大即可
- 第三种情况使用二分法mid=(c+d)/2 ,la=fmax(a,b,mid),ra=fmax(a,mid,d)
- 分别找数据前二分之一和后二分之一的值(通过递归求值,最后每一组的最小分化一定是两数比较,即:d-c=1
- 从而变为第二种方法求解最大值)最后返回la和ra的最大者。
Max(n)=Max(1+n/2)+Max(n/2,n)
#include<bits/stdc++.h> using namespace std; int a[1000]; int m; int fmax(int a[],int b,int c) { if(b==c) { return a[b]; } if(c-b==1) { int la=a[b]; int ra=a[c]; return (max(la,ra)); } if(c-b>=2) { int mid=(b+c)/2; int la=fmax(a,b,mid); int ra=fmax(a,mid,c); return (max(la,ra)); } } int fmin(int a[],int b,int c) { if(b==c) { return a[b]; } if(c-b==1) { int la=a[b]; int ra=a[c]; return (min(la,ra)); } if(c-b>=2) { int mid=(b+c)/2; int la=fmin(a,b,mid); int ra=fmin(a,mid,c); return (min(la,ra)); } } int main() { cout<<"请输入金块数量:"; cin>>m; cout<<"请输入每块金块的重量:"; for(int i=0;i<m;i++) { cin>>a[i]; } int maxn=fmax(a,0,m-1); int minn=fmin(a,0,m-1); cout<<"最重的金块:"<<maxn<<endl; cout<<"最轻的金块:"<<minn<<endl; }
3.二分法不相似情况
需要构造与原问题相似子问题。
问题:残缺棋盘
题目:(37条消息) 分治算法-残缺棋盘_gzj_1101的博客-CSDN博客_残缺棋盘
思路:
- 把棋盘二分为更小的棋盘,每次都切成四部分
- 由于原棋盘中只有一个特殊方格,这样划分后,这四个较小的棋盘中只有一个子棋盘包含特殊方格,其余三个棋盘中没有特殊方格。为了将这三个没有特殊方格的子棋盘转化为特殊棋盘,以便采用递归方法求解,可以用一个L型骨牌覆盖这三个较小棋盘的汇合处,从而转化为四个较小规模的棋盘覆盖问题。递归地使用这种划分策略,直到将棋盘分割为1*1的子棋盘
——《算法设计与分析——以acm大学程序竞赛在线题库为例》(清华大学出版社(北京))p68-69
//(tr,tc)为棋盘中右上角的方格坐标 //(dr,dc)为特殊方格的坐标 //size为行/列数 #include<bits/stdc++.h> using namespace std; int a[1024][1024]; int tr,dr,tc,dc,size=1,k,title=1; void fz(int tr,int tc,int dr,int dc,int size) { if(size==1)//结束标志:棋盘已经被划分为1*1 return; int s=size/2; //再次划分棋盘 int t=title++;//L型骨牌序号 //左上角 if(dr<tr+s&&dc<tc+s) { fz(tr,tc,dr,dc,s); } //若不在左上角则用t号L骨牌覆盖右下角(与其他子棋盘的交汇处) else { a[tr+s-1][tc+s-1]=t; //覆盖其余方格 fz(tr,tc,tr+s-1,tc+s-1,s); } //右上角 if(dr<tr+s&&dc>=tc+s) { fz(tr,tc+s,dr,dc,s); } else { a[tr+s-1][tc+s]=t; fz(tr,tc+s,tr+s-1,tc+s,s); } //左下角 if(dr>=tr+s&&dc<tc+s) { fz(tr+s,tc,dr,dc,s); } else { a[tr+s][tc+s-1]=t; fz(tr+s,tc,tr+s,tc+s-1,s); } //右下角 if(dr>=tr+s&&dc>=tc+s) { fz(tr+s,tc+s,dr,dc,s); } else { a[tr+s][tc+s]=t; fz(tr+s,tc+s,tr+s,tc+s,s); } } int main() { cin>>k; cin>>dr>>dc; for(int i=1;i<=k;i++) { size=size*2;//根据棋盘的阶数求出棋盘的行和列 } fz(0,0,dr,dc,size); for(int i=0;i<size;i++) { for(int j=0;j<size;j++) { printf("%-3d",a[i][j]); } printf("\n"); } return 0; }
//其他三个小棋盘的求法和左上方大体相同,只需要改变tr和tc,不做过多注释。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律