Java B组蓝桥杯第六届国赛:铺瓷砖

38 篇文章 1 订阅
32 篇文章 2 订阅

这101分的题真的搞不定。。。。。。。。

本人能力有限,搜遍百度,没有找到完美的代码。。。。。

倒是找到一堆文章在写多米诺骨牌版的铺地砖,但那些文章内容很杂,不知所云,无法耐心研究下去,但也许能类比到这道题目,但总觉得,那个多米诺骨牌和这个不是一个难度等级的。。。。

自己搞也没搞定,后来终于看到一位大佬的全排列代码,测试超时,花了好长改良了一下,还是超时,因为暴力全排列在本题不适用,但理论上还是没错的。。。思路倒是很简单的。。。

有兴趣可以看一下。。。。我加了注释。。。。。代码先贴出来。。。。。

注意,代码不是正确的代码,用的全排列,会的可以不用往下看了

如果是考试的话,下面的代码估计可以混到一小半的分数。

本人还正在努力找思路中。。。。。。。。。。。。。。。


标题:铺瓷砖

为了让蓝桥杯竞赛更顺利的进行,主办方决定给竞赛的机房重新铺放瓷砖。机房可以看成一个n*m的矩形,而这次使用的瓷砖比较特别,有两种形状,如【图1.png】所示。在铺放瓷砖时,可以旋转。
 
主办方想知道,如果使用这两种瓷砖把机房铺满,有多少种方案。

【输入格式】
输入的第一行包含两个整数,分别表示机房两个方向的长度。

【输出格式】
输出一个整数,表示可行的方案数。这个数可能很大,请输出这个数除以65521的余数。

【样例输入1】
4 4
【样例输出1】
2
【样例说明1】
这两种方案如下【图2.png】所示:
 
【样例输入2】
2 6
【样例输出2】
4
【数据规模与约定】
对于20%的数据,1<=n, m<=5。
对于50%的数据,1<=n<=100,1<=m<=5。
对于100%的数据,1<=n<=10^15,1<=m<=6。
 
资源约定:
峰值内存消耗(含虚拟机) < 512M
CPU消耗  < 8000ms


请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意:不要使用package语句。不要使用jdk1.7及以上版本的特性。
注意:主类的名字必须是:Main,否则按无效代码处理。
 

图1.png
图2.png

测试网址:https://www.dotcpp.com/oj/problem1827.html

代码注释应该很详细:

(1)创建数组作为地图,0为空,1为铺过地砖

(2)将两种地砖一共8种摆放形式也换成二维数组,0为空,1为砖体

(3)从地图第一块开始进行,8种依次尝试进行全排列。地图面积n*m,代码将会跑(n*m)^{8}次,面积越大,跑的时间越长

import java.util.Scanner;
public class Main {
	/* 这里用一个三维数组存储了8个二维数组
	 * 每个二维数组表示一种砖的一种放置方式,每个砖四种方式,一共8种,顺序任意
	 */
	int[][][] zhuan = { { { 1, 0 }, { 1, 1 } },
                        { { 1, 1 }, { 1, 0 } },
                        { { 1, 1 }, { 0, 1 } }, 
                        { { 0, 1 }, { 1, 1 } },
			            { { 1, 0 }, { 1, 1 }, { 1, 0 } },
                        { { 1, 1, 1 }, { 0, 1, 0 } }, 
                        { { 0, 1 }, { 1, 1 }, { 0, 1 } },
			            { { 0, 1, 0 }, { 1, 1, 1 } }
                      };
	/*
	 * 便于思考和想像,将地图换成二维数组
	 * n为地图纵向的长度(y轴)
	 * m为地图横向的长度(x轴)
	 */ 
	int n; 
	int m; 
	int sum = 0;// 累计结果

	public Main() {
		Scanner sn = new Scanner(System.in);
		n = sn.nextInt();
		m = sn.nextInt();
		int[][] map = new int[n][m];
		dfs(0, 0, 0, map);
		System.out.println(sum);
	}

	/*    
	 *  dfs(  地砖号    ,  放砖坐标x  ,  放砖坐标y  ,  地图    ) 
	 *  这里用最熟悉的坐标系(x,y)表示地图中的位置
	 *  每次铺砖都是以(x,y)为左上角开始铺砖
	 */
	public void dfs(int number, int x, int y, int[][] map) {
		if (y == n)
			return;// 超界
		// 坐标到达最后一位,且当前坐标的位置已被地砖填满,满足结果计数
		if (x == m - 1 && y == n - 1 && map[y][x] == 1) {
			sum++;
			sum%=65521;
			return;
		}
		// 全排列
		for (int i=number; i < zhuan.length; i++) {
			/*
			 * 这里分四种情况:
			 * 1.(x,y)无砖,以此为左上角,尝试每一种砖放置。
			 * 2.(x,y)有砖,但以此为左上角,存在一种砖也刚好可以放置。
			 * 3.(x,y)有砖,跳过该位置,以右边(x+1,y)为左上角进行尝试每一种砖放置。
			 * 4.(x,y)无砖,但又不存在某种砖可以放置,这种情况导致地图铺不满,在前3种情况的if中已经被筛去
			 *  其中for循环遍历时,2包含在1中
			 */
			// 如果可以放置地砖
			if (check(i, x, y, map)) {
				//获取放入地砖之后的新地图
				int[][] newmap = put(i, x, y, map);
				// 如果新地图(x,y)已经放置地砖
				if (newmap[y][x] == 1) {
					// 如果当前横向位置到达边界,换下一行开始放置
					// 没有的话继续执行
					if (x == m - 1)dfs(0, 0, y + 1, newmap);
					else dfs(0, x + 1, y, newmap);
				}
				//还原上面操作前的地图,方便下面使用
				map = recover(i, x, y, newmap);
			}
			// 如果当前选择的是第一种地砖,并且(x,y)被铺过
			if (i == 0 && map[y][x] == 1) {
				// 如果当前横向位置到达边界,换下一行开始放置
				// 没有的话继续执行
				if (x == m - 1)dfs(0, 0, y + 1, map);
				else dfs(0, x + 1, y, map);
			}
		}
	}

	// 检查当前(x,y)是否可以放置第i种地砖
	public boolean check(int number, int x, int y, int[][] di) {
		if (x + zhuan[number][0].length > m)// 超界
			return false;
		if (y + zhuan[number].length > n)// 超界
			return false;
		// 当前位置放入之后是否会与其他地砖产生重叠,只要有一处重叠,就立即返回false
		for (int i = y, iz = 0; iz < zhuan[number].length; i++, iz++)
			for (int j = x, jz = 0; jz < zhuan[number][iz].length; j++, jz++) {
				if (di[i][j] == 1 && zhuan[number][iz][jz] == 1)
					return false;
			}
		// 可以放置
		return true;
	}

	/*
	 * 熟悉java的基础知识的话,可以知道
	 * java的基本类型传参,内部传递的是值,而数组,类等传递的是对象,也就是内部地址
	 * 因此实际上我下面的put()和recover()不需要返回值
	 * 但便于理解,加上了返回值,无影响
	 */
    //在(x,y)上放入第number号地砖,返回修改后的地图
	public int[][] put(int number, int x, int y, int[][] map) {
		for (int i = y, iz = 0; iz < zhuan[number].length; i++, iz++)
			for (int j = x, jz = 0; jz < zhuan[number][iz].length; j++, jz++) {
				//如果地图的(i,j)为空,而地砖对应的(iz,jz)有砖,则修改地图(i,j)为有砖
				if (map[i][j] == 0 && zhuan[number][iz][jz] == 1) {
					map[i][j] = 1;
				}
			}
		return map;
	}

	// 还原   在(x,y)上铺第number号砖之前    的地图
	public int[][] recover(int number, int x, int y, int[][] map) {
		for (int i = y, iz = 0; iz < zhuan[number].length; i++, iz++)
			for (int j = x, jz = 0; jz < zhuan[number][iz].length; j++, jz++) {
				//如果地图的(i,j)与地砖对应的(iz,jz)都有砖,则修改地图(i,j)为空
				if (map[i][j] == 1 && zhuan[number][iz][jz] == 1) {
					map[i][j] = 0;
				}
			}
		return map;
	}

	public static void main(String[] args) {
		new Main();
	}

}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值