【算法设计与分析】实验一:棋盘覆盖问题 | 用JAVA实现地图染色覆盖效果( 时间复杂度+正确性证明)

实验1:棋盘覆盖问题

1.实验目的
(1)掌握递归与分治法的处理思路与算法框架。 (2)掌握应用递归与分治法解决具体问题的方法。
(3)掌握算法复杂度的理论分析与实验分析的联系,深刻体会算法复杂度作为算法的好坏评价指标的本质含义。

2.实验内容
【问题描述】在一个个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。显然,特殊方格在棋盘中能出现的位置有种,因而有种不同的棋盘,下面是时,16种棋盘中的一个。
在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
很显然,在任何一个的棋盘中,用到L型骨牌的个数恰为。 提出了这样一个问题:给定,设计实现一个算法实现棋盘的全覆盖。

【输入】输入一个特殊棋盘,确定其size; 输入一个特殊方格,确定其坐标。
【输出】 输出一个用不同颜色的L型骨牌覆盖后的棋盘。

3.算法描述及说明
(1)算法介绍
分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。递归的解这些子问题,然后将各子问题的解合并得到原问题的解。
分治法所能解决的问题一般具有以下几个特征:  
①该问题的规模缩小到一定的程度就可以容易地解决  
②该问题可以分解为若干个规模较小的相同问题,即其有最优子结构性质。  
③利用该问题分解出的子问题的解可以合并为该问题的解;  
④该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

分治法的基本步骤:(分治法在每一层递归上都有三个步骤)
分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
合并:将各个子问题的解合并为原问题的解。


> #分治法的一般的算法设计模式如下:   
> Divide-and-Conquer(P)
>   1. if |P|≤n0
>   2. then return(ADHOC(P))
>   3. 将P分解为较小的子问题 P1 ,P2 ,...,Pk
>   4. for i←1 to k
>   5. do yi ← Divide-and-Conquer(Pi)   △递归解决Pi
>   6. T ← MERGE(y1,y2,...,yk)   △合并子问题
>   7. return(T)   

其中:
|P|表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。
ADHOC§是该分治法中的基本子算法,用于直接解小规模的问题P。因此,当P的规模不超过n0时直接用算法ADHOC§求解。
算法MERGE(y1,y2,…,yk)是该分治法中的合并子算法,用于将P的子问题P1 ,P2 ,…,Pk的相应的解y1,y2,…,yk合并为P的解。

(2)覆盖思路
用分治策略,可以设计解棋盘问题的一个简捷的算法。
将的棋盘划分为4块这样的子棋盘, 对于第一次划分后的4块子棋盘,那么,特殊方格必位于4个子棋盘之一,其余3个子棋盘中无特殊方格。
用一个L型骨牌覆盖这3个较小的棋盘的汇合处,将这3个无特殊方格的子棋盘转化为特殊棋盘,从而将原问题化为4个较小规模的棋盘覆盖问题。同时,把特殊方格看成是黑色的,其余部分是白色的,题目就是要我们用黑色方块覆盖棋盘,递归求解。

(3)策略说明
那么,覆盖策略流程如下图。
图4:分治法解决棋盘覆盖问题流程图
设定一些初始变量:
int size ; //设置特殊棋盘的大小,size即长或宽的格子数 public
void search(int m, int n, int length) {…} //寻找特殊方格位置的函数
假设棋盘的左上角为顶点(0,0),如图所示。
图5:棋盘位置分布图

4.问题实例分析
以k=3为例进行分析,那么这是一个的特殊棋盘。 每划分一次,对交汇处进行L型骨牌填充,如下图所示。L型骨牌填充示意图 如上图所示,递归的使用这种分割,直至棋盘简化为棋盘即可。

5.算法时间复杂性及正确性分析

在这里插入图片描述

对于分治法的步骤中,关于合并的说明:
由于本题递归分解,将一个大规模问题分解为有限个小规模的问题(小问题之间相互独立,并且它们的问题性质和原始问题的问题性质相同、独立),分解的时候也是递归地分解,直至size==1。
也就是说,最后的每一个最小区域内,都只有一个L型骨牌和一个特殊方格,它们二者组成了最小的k=1,n=2的的最小特殊棋盘,将所有的这些棋盘按照分解的顺序,组合回去,即可得到最原始的棋盘的覆盖方案,即合并,其形式参考图6(b)。
一种算法的正确性一般包括3方面,即能否得出正确答案、其时间复杂度和空间复杂度。简单说明一些算法的证明过程中部分可作为通法的:递归部分通常需要数学证明中的数学归纳法,循环有类似的循环不变式。
最后,该算法满足准确性,可行性和有穷性的基本要求。
那么,这个问题是可以在有限的时间内快速地计算出来的。
因此,该算法是正确的。

6.运行结果展示及其说明
①当size=4,(m,n)=(2,1),该棋盘的覆盖方式如下:

在这里插入图片描述

②当size=8,(m,n)=(2,4),该棋盘的覆盖方式如下:

在这里插入图片描述

③当size=16,(m,n)=(13,13),该棋盘的覆盖方式如下:

在这里插入图片描述

说明:图7~图9中的黑色独立方块即为特殊方格。

7.心得体会
【关于问题本身】
我在求解这类问题时,经分析后,会发现求解过程相当复杂,或者直接求解法在时间上相当长,亦或者是根本无法直接求出。那么,我将会选择分治法。即先把它分解成几个子问题,找到求出这几个子问题的解法后,再找到合适的方法,把它们组合成求整个问题的解法。如果这些子问题还较大,难以解决,可以再把它们分成几个更小的子问题,以此类推,直至可以直接求出解为止。
当我在分解棋盘覆盖问题时,发现一定要是相互独立且与原问题性质相同子问题,否则求解起来将会更麻烦,甚至无法求解。也就是说,本题的技巧在于如何划分棋盘,使划分后的了棋盘的大小相同,并且每个了棋盘均包含一个特殊方格,从而将原问题分解为规模较小的棋盘覆盖问题。
【过程中遇到的问题】
开始实验时,并没有注意到棋盘的size大小问题,错误的设置了一个n为奇数的棋盘,结果就是输出多了一行一列的0。检查了半天的语法后,才发现是参数的设置问题。接着修改为n为偶数大小的棋盘后,顺利的完成了实验。
【感受与体会】
本次实验让我对分治法思想有了更深的了解,而且建立了算法复杂度的理论分析与实验分析的联系,进一步锻炼了自己的编程和独立思考问题的能力。

8.程序源代码
本题用Java语言实现了棋盘覆盖问题。下面是程序源代码展示。

/* 1、设置Element类,它有两个属性,一个是JPanel,它主要用来显示一个小格的颜色;另一个属性是flag,它用来标记此方格有没有被填充过。另外Element之所以要继承Jcomponent,是因为我们要把Element加入到JFrame当中去。因此具体代码如下:*/

package LiQiulin.main;

import javax.swing.JComponent;
import javax.swing.JPanel;

public class Element extends JComponent{
   
	//extends表示继承,这里是指class Element继承了javax.swing.JComponent类

	 private static final long serialVersionUID = 1L;
	//如果对这个类进行修改的话,它能被进行反序列化,而且不会报错,会版本向上兼容
	 private JPanel j;
	 //JPanel 是 Java图形用户界面(GUI)工具包swing中的面板容器类,可以加入到JFrame窗体中
	 boolean flag = false;
	 //flag为是否成功登录的一个标记,默认为 true:表示未登录登录成功时置为 false
	 
	 public Element() {
   
	       this.j = new JPanel(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

KirinLee_01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值