历届试题 剪格子  (DFS)

  历届试题 剪格子  

时间限制:1.0s   内存限制:256.0MB

      

问题描述

如下图所示,3 x 3 的格子中填写了一些整数。

+--*--+--+
|10* 1|52|
+--****--+
|20|30* 1|
*******--+
| 1| 2| 3|
+--+--+--+

我们沿着图中的星号线剪开,得到两个部分,每个部分的数字和都是60。

本题的要求就是请你编程判定:对给定的m x n 的格子中的整数,是否可以分割为两个部分,使得这两个区域的数字和相等。

如果存在多种解答,请输出包含左上角格子的那个区域包含的格子的最小数目。

如果无法分割,则输出 0。

输入格式

程序先读入两个整数 m n 用空格分割 (m,n<10)。

表示表格的宽度和高度。

接下来是n行,每行m个正整数,用空格分开。每个整数不大于10000。

输出格式

输出一个整数,表示在所有解中,包含左上角的分割区可能包含的最小的格子数目。

样例输入1

3 3
10 1 52
20 30 1
1 2 3

样例输出1

3

样例输入2

4 3
1 1 1 1
1 30 80 2
1 1 1 100

样例输出2

10

 分析:

  • 题目说好从左边的格子算起,所以首先开始就可以确定第一个格子在内
  • 由于所走的格子存在不确定性,所以要用到搜索的方法,可以用深搜
  • 为了减少程序负担,可以进行相关剪枝的优化
/**
*@author 杨雨婷
*@date 2019年5月8日
*/
package 剪格子;

import java.util.Scanner;

public class Main {
	static int m,n;
	static int a[][];
	static int sum = 0;
	static int min = 10000000;
	static boolean vis[][];
	static int b[][] = {{1,0},{-1,0},{0,1},{0,-1}};
	 
	public static void main(String[] args) {
		Scanner sca = new Scanner(System.in);
		m = sca.nextInt();
		n = sca.nextInt();
		a = new int[n][m];
		vis = new boolean[n][m];
		
		//输入部分
		for(int i = 0; i < n; i ++) {
			for(int j = 0;j < m; j ++) {
				a[i][j] = sca.nextInt();
				sum += a[i][j];
			}
		}
		//计算是否有结果
		if(sum % 2 != 0) {
			System.out.println(0);
		}
		else {
			//开始操作
			sum = sum / 2;
			vis[0][0] = true;
			DFS(a[0][0], 0, 0, 1);
			System.out.println(min);

		}
		
	}
/**
 * 
 * @param ns 所有走到的格子的和
 * @param x  格子所在行
 * @param y  格子所在列
 * @param num 所走格子数目
 */

	private static void DFS(int ns, int x, int y, int num) {
		//跳出递归
		if(ns == sum) {
			if(num < min) {
				min = num;
			}
		 
			return;
		}
		//剪枝
		if(ns > sum) {
			return ;
		}
		for(int i = 0; i < 4; i ++) {
			int t1 = x + b[i][0];
			int t2 = y + b[i][1];
			if( in(t1,t2) && !vis[t1][t2] ) {
		        vis[t1][t2] = true;
				DFS(ns + a[t1][t2], t1, t2, num + 1);
				vis[t1][t2] = false;
			}
		}
	}

	private static boolean in(int t1, int t2) {
		if(t1 >= 0 && t1 < n && t2 >= 0 && t2 < m) {
			return true;
		}
		return false;
	}

}

总结:

由于题目要求算最小的格子数,于是变回联想到bfs,感觉bfs可以使用,而且时间性能更好,但是实验过程却不尽人意,应为他当宽搜到该处的时候不知应该标记为访问过,还是不标记,因为他是被访问过,但是任然可供其他路线访问,他的路线不等同于走迷宫。走迷宫的时候当宽搜到此处便可标记为已访问,因为之前标记走到该位置的路径便是最短路径,所以可以用宽搜。

而这种计算每个点的和等于一个值的最小点数是与他有差异的,所以我认为bfs是走不通的。下面是我的错误代码。如果有哪位好友找到其中错误,欢迎指正。

/**
*@author 杨雨婷
*@date 2019年5月8日
*/
package 剪格子;

import java.util.ArrayList;
import java.util.Scanner;

public class Main3 {
	static int m,n;
	static int a[][];
	static int sum = 0;
	 
	static boolean vis[][];
	static int b[][] = {{1,0},{-1,0},{0,1},{0,-1}};

	public static void main(String[] args) {
		Scanner sca = new Scanner(System.in);
		m = sca.nextInt();
		n = sca.nextInt();
		a = new int[n][m];
		vis = new boolean[n][m];
		
		//输入部分
		for(int i = 0; i < n; i ++) {
			for(int j = 0;j < m; j ++) {
				a[i][j] = sca.nextInt();
				sum += a[i][j];
			}
		}
		//计算是否有结果
		if(sum % 2 != 0) {
			System.out.println(0);
		}
		else {
			node root = new node(1, 0, 0 ,a[0][0]);
			ArrayList<node> al = new ArrayList<>();
			al.add(root);
			while(!al.isEmpty()) {
				node h = al.get(0);
				int n = h.n;
				int x = h.x;
				int y = h.y;
				int ns = h.ns;
				al.remove(0);
				for(int i = 0; i < 4; i ++) {
					int t1 = x + b[i][0];
					int t2 = y + b[i][1];
					if( in(t1,t2) && !vis[t1][t2] ) {
						if(ns + a[t1][t2] == sum) {
							System.out.println(n + 1);
							return;
						}
						//剪枝
						if(ns + a[t1][t2] < sum) {
							 node t = new node(n + 1, t1, t2,ns + a[t1][t2] );
							 al.add(t);
							 vis[t1][t2] = true;
						}
				       
					}
				}
			}
		}

	}
	private static boolean in(int t1, int t2) {
		if(t1 >= 0 && t1 < n && t2 >= 0 && t2 < m) {
			return true;
		}
		return false;
	}

}
class node {
	int n;
	int x;
	int y;
	int ns;
	node(int n, int x , int y, int ns){
		this.n = n;
		this.x = x;
		this.y = y;
		this.ns = ns;
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值