这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,否则按无效代码处理。
测试网址:https://www.dotcpp.com/oj/problem1827.html
代码注释应该很详细:
(1)创建数组作为地图,0为空,1为铺过地砖
(2)将两种地砖一共8种摆放形式也换成二维数组,0为空,1为砖体
(3)从地图第一块开始进行,8种依次尝试进行全排列。地图面积n*m,代码将会跑次,面积越大,跑的时间越长
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();
}
}