蓝桥杯 18总决赛 B4 整理玩具(dfs/无dfs)

标题:整理玩具

小明有一套玩具,一共包含NxM个部件。这些部件摆放在一个包含NxM个小格子的玩具盒中,每个小格子中恰好摆放一个部件。

每一个部件上标记有一个0~9的整数,有可能有多个部件标记相同的整数。

小明对玩具的摆放有特殊的要求:标记相同整数的部件必须摆在一起,组成一个矩形形状。

如以下摆放是满足要求的:

00022
00033
44444

12244
12244
12233

01234
56789

以下摆放不满足要求:

11122
11122
33311

111111
122221
122221
111111

11122
11113
33333

给出一种摆放方式,请你判断是否符合小明的要求。

输入

输入包含多组数据。
第一行包含一个整数T,代表数据组数。 (1 <= T <= 10)
以下包含T组数据。
每组数据第一行包含两个整数N和M。 (1 <= N, M <= 10)
以下包含N行M列的矩阵,代表摆放方式。

输出

对于每组数据,输出YES或者NO代表是否符合小明的要求。

【样例输入】
3
3 5
00022
00033
44444
3 5
11122
11122
33311
2 5
01234
56789

【样例输出】
YES
NO
YES

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 1000ms

==============================

思路:
两种策略:

  • 遍历时候计算每个区间的最大长宽下的起点以及终点坐标,然后跟其他区间比较判断是否重合(重合即:有别的玩具混入区间)
    计算最大长宽下的两个坐标->遍历所有玩具(好在最大数据规模NM<=10,可行)
  • 遍历时候计算每个区间的外边的起点和终点坐标,判断这个区间是否存在别的玩具(其终点坐标也是下一个遍历的区间的起点坐标)
    遍历下一个区间的外边长宽(未知区间数量)->dfs

结论
运行的效率前者优于后者
原因:

  1. 第二种策略没有利用本题数据规模NM较小的条件,使用dfs搜索(基于区间数量未知的局面,这么做是必然的)
  2. 从具体的实现代码来看,第二种方法基于对未知区间的遍历,使得我不得不将下面的这种需要排除的局面考虑在内(使用一个数组去将出现过的数字计入),代码在实现的时候虽然不长但是类似的逻辑判断比较多,比较繁琐,第一种方法使用全集遍历,则没有边界未知的情况

使用

第一种策略:区间重复判断

public class Main{
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n =sc.nextInt();
		String[] ans =new String[n];	//单独做一个数组打印结果,避免输出结果和输入字符在一块显示
		char[][] a =null;
		int width =0 ,higth =0; 
		for(int i =0 ;i <n ;i ++) {
			higth =sc.nextInt();
			width =sc.nextInt();
			//记录数字出现
			boolean[] isIn =new boolean[10];
			//记录最大长宽
			int[][] sta_node =new int[10][2];		
			int[][] end_node =new int[10][2];
			//一个一个的扫描
			a =new char[higth][];
			int val =0;
			for(int y =0 ;y <higth ;y ++) {
				a[y] =sc.next().toCharArray();
				for(int x =0 ;x <width ;x ++) {
					val =a[y][x] -48;
					//初始坐标
					if(!isIn[val]) {
						isIn[val] =true; 
						end_node[val][0] =sta_node[val][0] =x; 
						end_node[val][1] =sta_node[val][1] =y; 
						continue;
					}
					//边界坐标
					end_node[val][0] =x; 
					end_node[val][1] =y;
				}
			}
			//找重叠
			boolean x_in =false ,y_in =false ,in =false;
			out:for(int j =0 ;j <=9 ;j ++) {
				if(!isIn[j]) continue;	//数字没有出现过
				for(int k =j +1 ;k <=9 ;k ++) {
					if(!isIn[k]) continue;	
					//重叠的逻辑判断有点复杂(j/k可以倒置)
					x_in =end_node[j][0] >=sta_node[k][0] &&sta_node[j][0] <=end_node[k][0];
					y_in =end_node[j][1] >=sta_node[k][1] &&sta_node[j][1] <=end_node[k][1];
					if(x_in &&y_in) {in =true; break out;}
				}
			}
			if(in) ans[i] ="NO"; else ans[i] ="YES";
		}
		sc.close();
		for(String s :ans) System.out.println(s);
	}
}

第二种策略:dfs摸索边长
值得吐槽的是:使用了好多数组,逻辑复杂了
(当时我自己的第一种实现的想法)

public class dfs_整理玩具_4 {
	static char[][][] c;
	static boolean[] b;
	static boolean[][][] isPlaced;
	static long t;
	public static void main(String[] args) {
		init();
		
		for(int n =0 ;n <c.length ;n ++) {
			b =new boolean[10];
			isPlaced[n][0][0] =true;
			if(dfs(c[n][0][0] ,0 ,0 ,n)) System.out.println("YES"); else System.out.println("NO");
		}
		
		System.out.println(System.currentTimeMillis() -t);
	}
	/**
	 * @param ele 正在遍历的元素
	 * @param y   
	 * @param x
	 * @param n   正在遍历的区块
	 * @return    该区块是否符合要求
	 */
	private static boolean dfs(char ele, int y, int x ,int n) {
		int y_tmp =c[n].length -1 ,x_tmp =c[n][0].length -1;
		for(int i =x +1 ;i <c[n][0].length ;i ++) {
			x_tmp=i;
			if(ele !=c[n][y][i]) {
				x_tmp --;
				if(!isPlaced[n][y][i]) if(!dfs(c[n][y][i] ,y ,i ,n)) return false;
				break;
			}
		}
		
		for(int i =y +1 ;i <c[n].length ;i ++) {
			y_tmp =i;
			if(ele !=c[n][i][x]) {
				y_tmp --;
				//(x <1 ||isPlaced[n][i][x -1]) -> 左侧同元素是否未遍历 这个感觉很蠢
				if(!isPlaced[n][i][x] &&(x <1 ||isPlaced[n][i][x -1])) if(!dfs(c[n][i][x] ,i ,x ,n)) return false;
				break;
			}
		}
		
		if(!check(c[n][y][x] ,y ,x ,y_tmp ,x_tmp ,n)) return false; b[c[n][y_tmp][x_tmp] -48] =true;
 		return true;
	}
	/**
	 * @param ele
	 * @param sta_y
	 * @param sta_x
	 * @param end_y
	 * @param end_x
	 * @param n
	 * @return
	 */
	private static boolean check(char ele, int sta_y, int sta_x, int end_y, int end_x, int n) {
		if(b[ele -48]) return false;
		for(int i =sta_x ;i <=end_x ;i ++) for(int j =sta_y ;j <=end_y ;j ++) { if(c[n][j][i] !=ele) return false; isPlaced[n][j][i] =true;}
		return true;
	}
	private static void init() {
		Scanner sc = new Scanner(System.in);
		int n =sc.nextInt();
		
		t =System.currentTimeMillis();
		
		c =new char[n][][];
		isPlaced =new boolean[n][][];
		int wid =0, hig =0;
		for(int i =0 ;i <n ;i ++) {
			hig =sc.nextInt();
			wid =sc.nextInt();
			c[i] =new char[hig][wid];
			isPlaced[i] =new boolean[hig][wid];
			for(int h =0 ;h <hig ;h ++) c[i][h] =sc.next().trim().toCharArray();
		}
		sc.close();
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

肯尼思布赖恩埃德蒙

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

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

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

打赏作者

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

抵扣说明:

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

余额充值