• 【算法设计zxd】第5章分治法


    目录

    分治算法策略的设计模式

    分治思想:

    分治算法求解问题的步骤:

    设计模式

    算法分析

    二分查找算法

    思考题

    计算模型:

    时间复杂度分析:

    代码:

    分治*大数乘法:

    【例5-2】设X, Y是两个n位的十进制数,求X*Y

     问题分析:

    1.1 计算方法:

     2.1 计算方法:

    思考题:

     算法分析:

    代码:

    思考题:二分治法 和 VS算法 矩阵相乘

    算法效率:

    代码:

     棋盘覆盖问题:

    【例5-4】残缺棋盘

    问题分析:s=size/2 分治

    计算模型​

    算法分析

    算法设计与描述:​

    代码: 

     选择性问题:——非等分

    一般性描述:

    问题分析:

    选择性问题:选第k小值

    计算模型

    算法分析

    代码:

    思考题:正元素 负元素排序

    计算模型:

    思考题:用分治法 求 数列的最大子段和


    分治算法策略的设计模式

    分治思想:

    把一个较大的问题分解成几个与原问题相似的 子问题,
    找到求出这几个子问题的解法后,
    再以适当的方法组织,把它们合成求整个问题的解。

    分治算法求解问题的步骤:

    (1) 分解:将要解决的问题划分成若干规模较小的同类问题;
    (2) 求解:当子问题划分得足够小时,用较简单的方法解决;
    (3) 合并:按原问题的要求,将子问题的解逐层合并构成原问题的解。

    设计模式

    算法分析

    用DC求解规模为n0 的问题耗费1个单位时间,用Merge合并 k个子问题的所需的时间为f(n)
    ,则分治算法的时间复杂度 为:

     根据主定理有

     

    原问题分为多少个子问题比较合适?每个子问题是否规模要多大?

    【子问题平衡原理】划分规模:等长>不等长规模 

    二分查找算法

    【例5-1】 设有n个元素的集合a[0]~a[n-1]是有序的,设计算
    法从这n个元素中找出值为x的特定元素。
    问题分析
    思想:将n个元素分成个数大致相同的两半,取a[n/2]与x作
    比较。如果x=a[n/2],则找到x,算法终止。如果x
    在集合的前半部分继续查找,否则在后半部分查找。
    计算模型
    (1)中间下标为mid =(left+right)/2。
    (2)找到待查数据,即有x==a[mid];
    (3)暂未查到数,若xa[mid],则
    left=mid+1。
    算法描述
    输入:x和n个元素的数组a[n]
    输出:与x匹配的数组元素下标或未找到(下标为-1)

    思考题

     

    计算模型:

    只剩一个数据时 max=x[low]

    对元素进行分组,中间下标mid=(low+high)/2,递归使用findmax(x,low,mid,tempmax1) ,findmax(x,mid+1,high,tempmax2) 返回两区间最大值tempmax1 和tempmax2

    最大值为tempmax1 和tempmax2 的较大值,

    时间复杂度分析:

    T(n) = 2*T(n/2) + 5; n>1

    T(n)=O(1) ;n=1

    设a=2 b=2 f(n)=5=n^(log_2 2-1)=O(1)   

    根据主定理 T(n)=O(n)

    代码:
     

    1. #include
    2. #include
    3. using namespace std;
    4. void findmax(int x[],int low,int high,int &max)
    5. {
    6. int tempmax1=0,tempmax2=0;
    7. if(low==high)
    8. {
    9. max = x[low] ;
    10. return ;
    11. }
    12. int mid=(low+high)/2;
    13. findmax(x,low,mid,tempmax1);
    14. findmax(x,mid+1,high,tempmax2);
    15. if(tempmax2< tempmax1)max=tempmax1;
    16. else max=tempmax2;
    17. }
    18. int main()
    19. {
    20. int n=10;
    21. int x[n];
    22. for(int i=0;i
    23. {
    24. x[i]=30+rand()%10;
    25. cout<" ";
    26. }
    27. int max=0;
    28. findmax(x,0,n-1,max);
    29. cout<
    30. return 0;
    31. }

    分治*大数乘法:

    【例5-2】设X, Y是两个n位的十进制数,求X*Y

     

     问题分析:

    1.1 计算方法

     1.2 时间复杂度:

     

     2.1 计算方法:

     

     2.2 时间复杂度:

     

     计算模型

    (1) 计算公式

     (2)递归结束条件

    当x=0或y=0时,结果为0。
    当n=1时,返回x*y 。

    思考题:

    算法描述

    输入:两个n位十进制数x,y 

    核心操作:三个乘法

    时间复杂度:T(n)=3*T(n/2)+c*n 

    设a=3,b=2 ,f(n)=cn, n^{log_2 3-log_2 3/2} = n,

    有f(n)=\Theta ( n^{log_2 3-log_2 3/2} )= \Theta (n),\varepsilon=log_2 3/2 >0 符合主定理(1),因此

     

     算法分析

    算法设计与描述算法分析
    输入:两个n位十进制数
    输出:乘积
    Karatsuba(x,y)
    {
        if(x==0||y==0)return 0;
        if(n==1)return x*y;
        n<- n/2;
        x1<- x / (int)pow(10,n) ,x0<- x % (int)pow(10,n);
        y1<- y/ (int)pow(10,n),y0<- y % (int)pow(10,n);
        //计算
        xy1<- Karatsuba(x1,y1);
        xy0<- Karatsuba(x0,y0);
        sum<- (x1-x0)*(y0-y1);
        return xy1*pow(10,(2*half) ) + (sum+xy1+xy0)*pow(10,half) +xy0;
    }

    (1)输入两个n位十进制数,

    (2)核心语句:将两数分为前后两部分x1 x0 y1 y0,递归计算

    (3)

     时间复杂度:T(n)=3*T(n/2)+c*n 

    设a=3,b=2 ,f(n)=cn, n^{log_2 3-log_2 3/2} = n,

    有f(n)=\Theta ( n^{log_2 3-log_2 3/2} )= \Theta (n),\varepsilon=log_2 3/2 >0 符合主定理(1),因此

     

    代码:

    1. #include<bits/stdc++.h>
    2. using namespace std;
    3. /*
    4. unsigned int 04294967295
    5. int -21474836482147483647
    6. unsigned long 04294967295
    7. long -21474836482147483647
    8. long long的最大值:9223372036854775807
    9. long long的最小值:-9223372036854775808
    10. unsigned long long的最大值:18446744073709551615
    11. __int64的最大值:9223372036854775807
    12. __int64的最小值:-9223372036854775808
    13. unsigned __int64的最大值:18446744073709551615
    14. */
    15. //Karatsuba方法 将大数拆分 两数位数相同
    16. //分治
    17. long long Karatsuba(long long num1,long long num2)
    18. {
    19. // cout<<num1<<" "<<num2<<endl;
    20. if(num1<10 ||num2<10)return num1*num2;
    21. int size1=ceil(log(num1)/log(10));//位数
    22. int size2=ceil(log(num2)/log(10));
    23. int half=max(size1,size2)/2;
    24. // cout<<size1<<" h "<<half<<endl;
    25. //拆分为 abcd
    26. long long x1=num1 / (int)pow(10,half) ,x0= num1 % (int)pow(10,half);
    27. long long y1=num2/ (int)pow(10,half),y0=num2 % (int)pow(10,half);
    28. // cout<<x1<<" "<<x0<<endl;
    29. //计算
    30. long long xy1=Karatsuba(x1,y1);
    31. long long xy0=Karatsuba(x0,y0);
    32. long long sum= (x1-x0)*(y0-y1);
    33. // cout<<pow(10,5)<<endl;
    34. return xy1*pow(10,(2*half) ) + (sum+xy1+xy0)*pow(10,half) +xy0;
    35. }
    36. int main()
    37. {
    38. cout<<endl<<"Karatsuba:"<<Karatsuba(155,999);//但是要注意 long long的数字限制
    39. return 0;
    40. }

    思考题:二分治法 和 VS算法 矩阵相乘

    算法效率:

    普通的二分治算法

    T(n)=O(1) n=2

    T(n)=8*T(n/2)+\Theta(n^2) n>2

    设a=8,b=2,f(n)=\Theta(n^2),n^(log_2 8 -1)=n^2.  

    有f(n)=\Theta(log_2 8 -1)=\Theta(n^2) 取 \varepsilon=1>0 

    根据主定理 T(n)=\Theta(n^3) 

    VS算法:【8次->7次乘法 m不唯一,但也增加了加法运算量】

    T(n)=O(1) n=2

    T(n)=7*T(n/2)+\Theta(n^2) 

    设a=7,b=2,f(n)=\Theta(n^2),n^(log_2 7 -1)=n^2.  

    有f(n)=\Theta(log_2 7 -0.8)=\Theta(n^2) 取\varepsilon =0.8>0  

    根据主定理 T(n)=\Theta(n^2.8)

    7个乘法,18个加法 

    因此VS算法比普通二分治算法效率高

    代码:

    1. #include
    2. using namespace std;
    3. class Mat{
    4. public:
    5. int **m;
    6. int n;
    7. Mat(int nn){
    8. // cout<<"Mat:"<
    9. n=nn;
    10. m=new int*[n];
    11. for(int i=0;i
    12. {
    13. m[i]=new int[n];
    14. }
    15. for(int i=0;i
    16. {
    17. for(int j=0;j
    18. {
    19. m[i][j]=0;
    20. }
    21. }
    22. // cout<<"over"<
    23. }
    24. void input01(int** x)
    25. {
    26. int n=this->n;
    27. for(int i=0;i
    28. {
    29. for(int j=0;j
    30. {
    31. this->m[i][j]=x[i][j];
    32. }
    33. }
    34. }
    35. void show()
    36. {
    37. for(int i=0;i
    38. {
    39. for(int j=0;j
    40. {
    41. cout<"\t";
    42. }
    43. cout<
    44. }cout<
    45. }
    46. Mat operator + (Mat &a)// 定义重载运算符"+"的友元函数
    47. {
    48. Mat c(a.n);
    49. for(int i=0;i
    50. {
    51. for(int j=0;j
    52. {
    53. c.m[i][j]=a.m[i][j]+this->m[i][j];
    54. }
    55. }
    56. return c;
    57. }
    58. Mat operator - (Mat &a)// 定义重载运算符"-"的友元函数
    59. {
    60. Mat c(a.n);
    61. for(int i=0;i
    62. {
    63. for(int j=0;j
    64. {
    65. c.m[i][j]=this->m[i][j] -a.m[i][j];
    66. }
    67. }
    68. return c;
    69. }
    70. };
    71. int ** change(int x[][4])
    72. {
    73. int n=4;
    74. // int **xx=malloc(4*sizeof(int*));
    75. int **xx=new int*[4];
    76. for(int i=0;i
    77. {
    78. xx[i]=new int[4];
    79. for(int j=0;j
    80. {
    81. xx[i][j]=x[i][j];
    82. cout<"\t";
    83. }
    84. cout<
    85. }
    86. cout<
    87. return xx;
    88. }
    89. int a[4][4]={
    90. 1,0,2,1,
    91. 4,1,1,0,
    92. 0,1,3,0,
    93. 5,0,2,1
    94. };
    95. int **aa=change(a);
    96. int b[4][4]={
    97. 0,1,0,1,
    98. 2,1,0,4,
    99. 2,0,1,1,
    100. 1,3,5,0
    101. };
    102. int **bb=change(b);
    103. int n=4;
    104. Mat ma(n),mb(n),mc01(n),mc02(n);
    105. //二分治法
    106. void bs(int n,Mat a,Mat b,Mat c)
    107. {
    108. // cout<
    109. if(n==1)
    110. {
    111. c.m[0][0]=ma.m[0][0] * mb.m[0][0];
    112. // cout<<"n==1"<
    113. return ;
    114. }
    115. else if(n==2)
    116. {
    117. // cout<<"n==2"<
    118. c.m[0][0]=a.m[0][0]*b.m[0][0]+a.m[0][1]*b.m[1][0];
    119. c.m[0][1]=a.m[0][0]*b.m[0][1]+a.m[0][1]*b.m[1][1];
    120. c.m[1][0]=a.m[1][0]*b.m[0][0]+a.m[1][1]*b.m[1][0];
    121. c.m[1][1]=a.m[1][0]*b.m[0][1]+a.m[1][1]*b.m[1][1];
    122. // cout<<"n==2"<
    123. return ;
    124. }
    125. //划分为4个矩阵
    126. Mat a00(n/2),a01(n/2),a10(n/2),a11(n/2);
    127. Mat b00(n/2),b01(n/2),b10(n/2),b11(n/2);
    128. // cout<<"划分"<
    129. for(int i=0;i2;++i)
    130. {
    131. for(int j=0;j2;++j)
    132. {
    133. //左上
    134. // cout<<"左上"<
    135. a00.m[i][j]=a.m[i][j];
    136. b00.m[i][j]=b.m[i][j];
    137. // cout<<"右上"<
    138. //右上
    139. a01.m[i][j]=a.m[i][j+n/2];
    140. b01.m[i][j]=b.m[i][j+n/2];
    141. //左下
    142. a10.m[i][j]=a.m[i+n/2][j];
    143. b10.m[i][j]=b.m[i+n/2][j];
    144. //右下
    145. a11.m[i][j]=a.m[i+n/2][j+n/2];
    146. b11.m[i][j]=b.m[i+n/2][j+n/2];
    147. }
    148. }
    149. // cout<<"递归"<
    150. //递归求四个方阵
    151. Mat ab0000(n/2);
    152. bs(n/2,a00,b00,ab0000);
    153. Mat ab0110(n/2);
    154. bs(n/2,a01,b10,ab0110);
    155. Mat ab0001(n/2);
    156. bs(n/2,a00,b01,ab0001);
    157. Mat ab0111(n/2);
    158. bs(n/2,a01,b11,ab0111);
    159. Mat ab1000(n/2);
    160. bs(n/2,a10,b00,ab1000);
    161. Mat ab1110(n/2);
    162. bs(n/2,a11,b10,ab1110);
    163. Mat ab1001(n/2);
    164. bs(n/2,a10,b01,ab1001);
    165. Mat ab1111(n/2);
    166. bs(n/2,a11,b11,ab1111);
    167. Mat c00(n/2),c01(n/2),c10(n/2),c11(n/2);
    168. c00=ab0000+ab0110;
    169. c01=ab0001+ab0111;
    170. c10=ab1000+ab1110;
    171. c11=ab1001+ab1111;
    172. // c00 =m1+m4-m5+m7;
    173. // c01 =m3+m5;
    174. // c10=m2+m4;
    175. // c11=m1+m3-m2+m6;
    176. //合并
    177. for(int i=0;i2;++i)
    178. {
    179. for(int j=0;j2;++j)
    180. {
    181. c.m[i][j]=c00.m[i][j];
    182. c.m[i][j+n/2]=c01.m[i][j];
    183. c.m[i+n/2][j]=c10.m[i][j];
    184. c.m[i+n/2][j+n/2]=c11.m[i][j];
    185. }
    186. }
    187. }
    188. //VS算法
    189. void vs(int n,Mat a,Mat b,Mat c)
    190. {
    191. // cout<
    192. if(n==1)
    193. {
    194. c.m[0][0]=ma.m[0][0] * mb.m[0][0];
    195. // cout<<"n==1"<
    196. return ;
    197. }
    198. else if(n==2)
    199. {
    200. // cout<<"n==2"<
    201. c.m[0][0]=a.m[0][0]*b.m[0][0]+a.m[0][1]*b.m[1][0];
    202. c.m[0][1]=a.m[0][0]*b.m[0][1]+a.m[0][1]*b.m[1][1];
    203. c.m[1][0]=a.m[1][0]*b.m[0][0]+a.m[1][1]*b.m[1][0];
    204. c.m[1][1]=a.m[1][0]*b.m[0][1]+a.m[1][1]*b.m[1][1];
    205. // cout<<"n==2"<
    206. return ;
    207. }
    208. //划分为4个矩阵
    209. Mat a00(n/2),a01(n/2),a10(n/2),a11(n/2);
    210. Mat b00(n/2),b01(n/2),b10(n/2),b11(n/2);
    211. // cout<<"划分"<
    212. for(int i=0;i2;++i)
    213. {
    214. for(int j=0;j2;++j)
    215. {
    216. //左上
    217. // cout<<"左上"<
    218. a00.m[i][j]=a.m[i][j];
    219. b00.m[i][j]=b.m[i][j];
    220. // cout<<"右上"<
    221. //右上
    222. a01.m[i][j]=a.m[i][j+n/2];
    223. b01.m[i][j]=b.m[i][j+n/2];
    224. //左下
    225. a10.m[i][j]=a.m[i+n/2][j];
    226. b10.m[i][j]=b.m[i+n/2][j];
    227. //右下
    228. a11.m[i][j]=a.m[i+n/2][j+n/2];
    229. b11.m[i][j]=b.m[i+n/2][j+n/2];
    230. }
    231. }
    232. // cout<<"递归"<
    233. //递归求四个方阵
    234. int half=n/2;
    235. //m1=(a00+a11)*(b00+b11)
    236. Mat m1(n/2);
    237. vs(n/2,a00+a11,b00+b11,m1);
    238. // cout<<"m1"<
    239. //m2=(a10+a11)*(b00)
    240. Mat m2(n/2);
    241. vs(n/2,a10+a11,b00,m2);
    242. //m3=(a00)*(b01-b11)
    243. Mat m3(n/2);
    244. vs(n/2,a00,b01-b11,m3);
    245. //m4=(a11)*(b10-b00)
    246. Mat m4(n/2);
    247. vs(n/2,a11,b10-b00,m4);
    248. //m5=(a00+a01)*(b11)
    249. Mat m5(n/2);
    250. vs(n/2,a00+a01,b11,m5);
    251. //m6=(a10-a00)*(b00+b01)
    252. Mat m6(n/2);
    253. vs(n/2,a10-a00,b00+b01,m6);
    254. //m7=(a01-a11)*(b10+b11)
    255. Mat m7(n/2);
    256. vs(n/2,a01-a11,b10+b11,m7);
    257. // cout<<"finish m1-7"<
    258. Mat c00(n/2),c01(n/2),c10(n/2),c11(n/2);
    259. c00 =m1+m4-m5+m7;
    260. c01 =m3+m5;
    261. c10=m2+m4;
    262. c11=m1+m3-m2+m6;
    263. //合并
    264. for(int i=0;i2;++i)
    265. {
    266. for(int j=0;j2;++j)
    267. {
    268. c.m[i][j]=c00.m[i][j];
    269. c.m[i][j+n/2]=c01.m[i][j];
    270. c.m[i+n/2][j]=c10.m[i][j];
    271. c.m[i+n/2][j+n/2]=c11.m[i][j];
    272. }
    273. }
    274. }
    275. int main()
    276. {
    277. ma.input01(aa);
    278. mb.input01(bb);
    279. vs(n,ma,mb,mc01);
    280. mc01.show();
    281. bs(n,ma,mb,mc02);
    282. mc02.show();
    283. return 0;
    284. }
    285. /*
    286. 1 0 2 1
    287. 4 1 1 0
    288. 0 1 3 0
    289. 5 0 2 1
    290. 0 1 0 1
    291. 2 1 0 4
    292. 2 0 1 1
    293. 1 3 5 0
    294. 5 4 7 3
    295. 4 5 1 9
    296. 8 1 3 7
    297. 5 8 7 7
    298. 5 4 7 3
    299. 4 5 1 9
    300. 8 1 3 7
    301. 5 8 7 7
    302. */

     棋盘覆盖问题

    【例5-4】残缺棋盘

    残缺棋盘是一个有2^ k ×2^ k (k≥1)个方格的棋盘,其中恰
    有一个方格残缺。如图给出k=1时各种可能的残缺棋盘,其
    中残缺的方格用阴影表示。

    这样的棋盘称作“三格板”,残缺棋盘问题就是要用这四
    种三格板覆盖更大的残缺棋盘。在此覆盖中要求:
    1)两个三格板不能重叠
    2)三格板不能覆盖残缺方格,但必须覆盖其他所有的方格
    在这种限制条件下,所需要的三格板总数为(2^ k ×2^ k -1 )/3。【(方格数-1)/3】

    问题分析:s=size/2 分治

    分4种情况:
    1. 若坏格子在左上角,则把图5-2中①号放置在二分后的中间
    位置,如图(2)
    2. 若坏格子在右上角,则把图5-2中②号放置在二分后的中间
    位置,如图(3)
    3. 若坏格子在左下角,则把图5-2中③号放置在二分后的中间
    位置,如图 (4)
    4. 若坏格子在右下角,则把图5-2中④号置在二分后的中间位
    置,如图(5)

    如何确定棋盘,如何区分棋盘?

    ——用棋盘左上角进行定位,用棋盘边长划定范围。

    计算模型

    (1)棋盘:用二维数组CBoard[n][n]存储
    (2)棋盘的识别
    • tr 棋盘中左上角方格所在行。
    • tc 棋盘中左上角方格所在列。
    • dr 残缺方块所在行。
    • dc 残缺方块所在列。
    • size 棋盘的行数或列数。
    覆盖残缺棋盘所需要的三格板数目为:( size^ 2 -1 ) / 3
    将这些三格板编号为1到( size^ 2 -1 ) / 3。则将残缺棋
    盘三格板编号的存储在数组 CBoard[n][n] 的对应位置中

    (3)计算过程
    设s=size/2,当s或size<2时,覆盖结束。
    • 当 行坐标dr时,坏格在左上子棋盘
    中,用①(tr+s-1, tc+s);(tr+s, tc+s-1) ; (tr+s, tc+s)。//红色部分

    • 当dr

    中,用② (tr+s-1, tc+s-1); (tr+s, tc+s-1) ; (tr+s, tc+s)。
    • 当dr≥tr+s and dc
    中,用③(tr+s-1, tc+s-1); (tr+s-1, tc+s) ; (tr+s, tc+s)。

    • 当dr≥tr+s and dc≥tc+s时,坏格在右下子棋盘 中,

    用④ (tr+s-1, tc+s-1); (tr+s-1, tc+s) ; (tr+s, tc+s-1)。

    算法分析

     

     

    算法设计与描述:

    代码: 

    1. #include
    2. using namespace std;
    3. int n=8;//需要是2^x
    4. int amount=1;
    5. //棋盘
    6. int CBoard[100][100];
    7. //坏格子坐标
    8. //覆盖之后棋盘
    9. int chessboard[100][100];
    10. //左上角方格所在行int tr,左上角 列int tc, (tr.tc)
    11. //残缺 行int dr,残缺 列int dc (dr,dc )
    12. //棋盘的行数or列数int size)
    13. void CBCover(int CBoard[][100],int tr,int tc,int dr,int dc,int size)
    14. {
    15. if(size<2)return;
    16. int t=amount;
    17. amount++;//所使用的三格板的数目
    18. //二分
    19. int s=size/2;//子问题的棋盘
    20. //残缺方格位于左上棋盘 1号隔板
    21. if(dr
    22. {
    23. //递归
    24. CBCover(CBoard,tr,tc,dr,dc,s);
    25. //到最接近的 结束
    26. //覆盖这三块方格 1号隔板
    27. CBoard[tr+s-1][tc+s]=t;//注意是相对于隔板的标记坐标
    28. CBoard[tr+s][tc+s-1]=t;//不是相对于残格的坐标
    29. CBoard[tr+s][tc+s]=t; //
    30. //覆盖其余部分
    31. //下侧棋盘 覆盖的那块隔板 会在新的棋盘上占用一个方格 也就是一个坏块
    32. CBCover(CBoard,tr,tc+s,tr+s-1,tc+s,s);
    33. //右侧
    34. CBCover(CBoard,tr+s,tc,tr+s,tc+s-1,s);
    35. //右下侧
    36. CBCover(CBoard,tr+s,tc+s,tr+s,tc+s,s);
    37. }
    38. //残缺方格位于右上棋盘 2号隔板
    39. else if(dr=tc+s )
    40. {
    41. //递归
    42. CBCover(CBoard,tr,tc+s,dr,dc,s);
    43. CBoard[tr+s-1][tc+s-1]=t;//覆盖2号三格板
    44. CBoard[tr+s][tc+s-1]=t;
    45. CBoard[tr+s][tc+s]=t;
    46. //覆盖其余部分
    47. CBCover(CBoard,tr,tc,tr+s-1,tc+s-1,s);
    48. CBCover(CBoard,tr+s,tc,tr+s,tc+s-1,s);
    49. CBCover(CBoard,tr+s,tc+s,tr+s,tc+s,s);
    50. }
    51. //残缺方格位于左下棋盘 3号隔板
    52. else if(dr>= tr+s && dc < tc+s )
    53. {
    54. //递归
    55. CBCover(CBoard,tr+s,tc,dr,dc,s);
    56. CBoard[tr+s-1][tc+s-1]=t;//上
    57. CBoard[tr+s-1][tc+s]=t;//右上
    58. CBoard[tr+s][tc+s]=t;//右
    59. //覆盖其余部分
    60. CBCover(CBoard,tr,tc,tr+s-1,tc+s-1,s);
    61. CBCover(CBoard,tr,tc+s,tr+s-1,tc+s,s);
    62. CBCover(CBoard,tr+s,tc+s,tr+s,tc+s,s);
    63. }
    64. //残缺方格位于右下棋盘 4号隔板
    65. else if(dr>=tr+s && dc>=tc+s )
    66. {
    67. //递归
    68. CBCover(CBoard,tr+s,tc+s,dr,dc,s);
    69. CBoard[tr+s-1][tc+s-1]=t;//覆盖2号三格板
    70. CBoard[tr+s-1][tc+s]=t;
    71. CBoard[tr+s][tc+s-1]=t;
    72. //覆盖其余部分
    73. CBCover(CBoard,tr,tc,tr+s-1,tc+s-1,s);
    74. CBCover(CBoard,tr,tc+s,tr+s-1,tc+s,s);
    75. CBCover(CBoard,tr+s,tc,tr+s,tc+s-1,s);
    76. }
    77. }
    78. void output(int a[][100])
    79. {
    80. cout<<"a:"<
    81. for(int i=0;i
    82. {
    83. for(int j=0;j
    84. {
    85. cout<"\t";
    86. }
    87. cout<
    88. }
    89. }
    90. int main()
    91. {
    92. output(CBoard);
    93. CBCover(CBoard,0,0,0,0,n);
    94. output(CBoard);
    95. return 0;
    96. }

     选择性问题:——非等分

    选最大值、最小值、选中位数、选第二大值

    一般性描述:

    设A是含有n个元素的集合,从A中选出第k小的元素,其中
    1≤k≤n。这里的第k小是指当A中元素从小到大排序之后,
    第k个位置的元素,当k=1时,选出的是A中的最小值,当
    k=n时,选出的就是最大值。

    问题分析:

    (1)排序--找第k小的元素,时间渐近复杂度为O(nlogn)——高级排序算法 极限
    (2)蛮力算法--当k=1或k=n时【最大或最小】,一趟即可找到解,时间渐近复杂度只有O(n)。
    (3)二分治策略--当k=2时,将原数列分为两个子集,每个子 集各选出一个最小值和第二小值,从这个4个数字里可找到当前的最小值

    选择性问题:选第k小值

    计算模型

    选择pivot=a[left]值,j=right对集合进行二分
    (1) j-left=k-1,则分界数据就是选择问题的答案
    (2) j-left>k-1,则选择问题的答案继续在左子集中找,问题规模变小了。
    (3) j-left

    算法分析

    (1)最坏情况下的复杂性是O(n 2 ),left 总是为空,第k个元素总是位于 right
    子集中。
    (2)设n=2 k ,算法的平均复杂性是O (n+logn)。若仔细地选择分界元素,则
    最坏情况下的时间开销也可以变成O(n)。
    (3)它与本章第一个例子非常相似,只对一半子集进行搜索,所不同的时,
    由于pivot点选择的不同,多数情况下它是非等分的。
    算法设计与分析
    输入
    输出

    int select(int a[],int left,int right,int k)
    {
        int i,j,pivot,t;
        if(left>=right)return a[left];
        i=left;
        j=right+1;
        pivot=a[left];
        while(true)
        {
            do{
                i++;
            }
            while(a[i]         if(i>=j)break;
            t=a[i];
            a[i]=a[j];
            a[j]=t;
            
        }
        if(j-left+1==k) return pivot;
        a[left]=a[j];
        a[j]=pivot;
        if(k<(j-left+1))select(a,left,j-1,k);
        else select(a,j+1,right,k-j-j+left);

    代码:

    1. #include
    2. using namespace std;
    3. void show(int a[])
    4. {
    5. cout<<"a[]:";
    6. for(int i=0;i<8;i++)
    7. cout<"\t";
    8. cout<
    9. }
    10. int select(int a[],int left,int right,int k)
    11. {
    12. cout<"select:left:"<" right:"<" k: "<
    13. cout<<"left不变 right不变"<
    14. show(a);
    15. int i,j,pivot,t;
    16. if(left>=right) {
    17. cout<<"left>=right a[left]"<
    18. return a[left];
    19. }
    20. i=left;
    21. j=right+1;
    22. pivot=a[left];
    23. cout<<"left:"<" a[left] = pivot: "<
    24. while(true)
    25. {
    26. cout<"in while:"<
    27. do{
    28. i++;
    29. }
    30. while(a[i]
    31. do{
    32. j--;
    33. }while(a[j]>pivot);
    34. cout<<"now pivot is "<" 从left到i都小于pivot "<":"<" 从j到right都大于pivot "<":"<
    35. if(i>=j){
    36. cout<<"i>=j break;"<
    37. break;
    38. }
    39. t=a[i];
    40. a[i]=a[j];
    41. a[j]=t;
    42. cout<<"swap "<" "<
    43. show(a);
    44. }
    45. cout<"while结束 此时 j left k 是"<" "<" "<
    46. cout<"最开始right-left+1等于总的元素个数,现在j往前移动 也就是比pivot大的数有(right-j)个"<
    47. <<"选第k小的元素 应该是有sum1=k-1个数比x小,有sum2=right-left+1-k个数比x大"<
    48. <<"此时 sum2=(right-j) 那么k=right-left+1-(right-j) =j-left+1 也就是此时是第(j-left+1)小 "<
    49. <<"需要让此时的 (j-left+1)==k 就求出了答案 "<
    50. if(j-left+1==k) return pivot;
    51. cout<<"然而 (j-left+1)!=k 需要继续寻找 "<
    52. cout<<"a[left]是基准元素,j小于等于基准,因此交换 "<
    53. cout<<"交换 left j 的元素"<
    54. a[left]=a[j];
    55. a[j]=pivot;
    56. show(a);
    57. if(k<(j-left+1))
    58. {
    59. cout<"左侧";
    60. select(a,left,j-1,k);
    61. }
    62. else
    63. {
    64. cout<"右侧";
    65. select(a,j+1,right,k-j-1+left);
    66. }
    67. }
    68. int a[]={1,4,5,3,2,6,8,7};
    69. int k=2;
    70. int main()
    71. {
    72. int left=0;
    73. int right=7;
    74. int res= select(a,left,right,k);
    75. cout<
    76. return 0;
    77. }

    思考题:正元素 负元素排序

    问题分析:
    使数组中所有负数位于正数之前,空间效率:原数组的空间不可改变,临时变量尽可能少,在原数组上改变 不增加结果数组。时间效率:运算次数尽可能少。

    一定会遍历一遍数组,且每个元素与0进行比较,因此从两侧开始,若左侧小于0 不变,大于0需要后移,右侧大于0不变,小于0需要后移。因此找到两位置left right ,进行交换。再以此为范围进行下一轮递归,直到left>=right 结束递归。

    计算模型:

    从[left,right]范围内进行二分。

    (1)left>=right 结束循环

    (2)从left开始找到第一个大于0的元素下标,更新left

    (3)从right开始找到第一个小于0的元素下标,更新right

    (4)若left>=right 结束循环

    (5)否则从新的【left,right】范围进行递归排序

    算法设计与分析

    算法设计与描述

    算法分析
    输入:数组a[]
    输出:排序后数组

    void fun(int a[],int left,int right)
    {
        if(left>=right) {
            return ;
        }
        while(a[left]<0) ++left;
        
        while(a[right]>0) --right;

        if(left     {
            t=a[left];
            a[left]=a[right];
            a[right]=t;
            fun(a,left,right);
        }
        else return ;
    }

    (1)输入规模n
    (2)核心语句:比较和交换语句

    (3)

    时间复杂度 按最坏情况 每交换一次 都只能纠正2个元素的位置,则需要递归n/2次 :

    T(n)= T(n-2) + c

    =T(n-4) + 2*c

    =T(n-6)+3*c

    共n/2次 T(n)=O(n)

    空间复杂度:则S(n)= n/2 +2;

     

    思考题:用分治法 求 数列的最大子段和

  • 相关阅读:
    什么是war包?war包该怎么运行?
    嵌入式学习笔记(16)反汇编工具objdump
    Linux系统管理
    Pandas 2.1中的新改进和新功能
    Bash变量--位置参数变量
    深圳市第十二届职工技术创新运动会暨2022年深圳技能大赛—集成电路应用开发职业技能竞赛
    电阻值的优先值
    java设计模式之命令设计模式的前世今生
    【Pytest实战】Pytest+Allure+Jenkins自动化测试框架搭建
    Linux内核设计与实现 第九章 内核同步与介绍
  • 原文地址:https://blog.csdn.net/iivan_cpp/article/details/123517470