Java B组蓝桥杯第十届国赛:估计人数

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

试题 I: 估计人数
时间限制: 1.0s 内存限制: 512.0MB 本题总分:25 分
【问题描述】
给定一个 N × M 的方格矩阵,矩阵中每个方格标记 0 或者 1 代表这个方格
是不是有人踩过。
已知一个人可能从任意方格开始,之后每一步只能向右或者向下走一格。
走了若干步之后,这个人可以离开矩阵。这个人经过的方格都会被标记为 1,
包括开始和结束的方格。注意开始和结束的方格不需要一定在矩阵边缘。
请你计算至少有多少人在矩阵上走过。
【输入格式】
输入第一行包含两个整数 N、M。
以下 N 行每行包含 M 个整数 (0/1),代表方格矩阵。
【输出格式】
输出一个整数代表答案。
【样例输入】
5 5
00100
11111
00100
11111
00100
【样例输出】
3

【数据规模与约定】
对于所有评测用例,1 ≤ N, M ≤ 20,标记为 1 的方格不超过 200 个。

诶,明天就该国赛了,今天让这道题浪费一天,不过收获很大,这个题对于没学过算法的我比较难,还好在网上找到了相关资料,琢磨一下午,刚刚写出来。

思路:

这是一道典型的“可相交的最小路径覆盖”问题。

大概解法:

(1)先把 “可相交的最小路径覆盖” 问题转化成 “不相交的最小路径覆盖” 问题。

       (可以通过弗洛伊德算法转换)

(2)然后求出“不相交的最小路径覆盖”问题的最小路径数

       (最小路径数=图中节点总数-节点最大匹配度)

(3)求(2)的重点是求出节点最大匹配度。而节点最大匹配度可以通过匈利亚算法求出。

(4)打完收工。

由于时间关系,暂时就不很细致的讲解了,给出参考文章

参考大佬文章,通俗易懂。

1、 “可相交的最小路径覆盖” 和“不相交的最小路径覆盖”的概念:https://blog.csdn.net/qq_39627843/article/details/82012572

2:、弗洛伊德算法:https://www.cnblogs.com/wangyuliang/p/9216365.html

3、匈利亚算法:https://blog.csdn.net/Arabic1666/article/details/79824390

简单看完这三篇文章,有了概念之后,根据上方解法过程,结合我的代码和注释,很容易搞定。

代码:

import java.util.Scanner;

public class Main {
	
	/*             可相交的最小路径覆盖                    */
	// n行m列
	int n, m;
	//地图
	char[][] matrix;
	//地图有多少个可以走的点
	int pointcount = 0;
	//连接表,记录每个点到任意一个点是否走到,构成一个二维矩阵
	boolean[][] con;
	//给每一个点标上序号,第一个点序号为1,跟地图位置对应
	//功能:作为索引用于转换 连接表con
	int[][] num;
	
	/*  匈牙利算法的工具    */
	//匹配表,存放匹配关系,如:一个点a匹配另一个点b,但b有被其他点匹配过,都在这个表中对应
	int[]  matchtable;
	//点b是否被匹配过
	boolean[] ismatch;

	
	public Main() {
		Scanner sn = new Scanner(System.in);
		n = sn.nextInt();
		m = sn.nextInt();
		matrix = new char[n][m];
		num=new int[n][m];
		sn.nextLine();
		//读取地图
		for (int i = 0; i < n; i++) {
			matrix[i] = sn.nextLine().trim().toCharArray();
			for (int j = 0; j < m; j++) {
				//记录序号,从1开始的
				if (matrix[i][j] == '1')num[i][j]=++pointcount;
			}
		}
	
		//设置连接表大小,0位置不能用,因此需要pointcount+1的行和列
		con=new boolean[pointcount+1][pointcount+1];
		//根据序号开始转换连接表
		for(int i=0;i<n-1;i++){
	        for(int j=0;j<m-1;j++){
	        	//如果可以向下走或者向右走,说明两个点之间存在直接连接关系,对应到con上
	        	if(matrix[i][j]=='1'&&matrix[i][j+1]=='1')
	        		con[num[i][j]][num[i][j+1]]=true;
	        	if(matrix[i][j]=='1'&&matrix[i+1][j]=='1')
	        		con[num[i][j]][num[i+1][j]]=true;
	        }
		}
			
		/*   弗洛伊德算法    */
		/* 以k为中间点,遍历所有点是否能够使得con表中原本不能直接相连的点a和点b
		 * 但通过k点,可以将a和b连接起来。
		 * 比如k在a的右边,b在k的下边,那通过k就可以使得a,b连接
		 */
		for(int k=1;k<=pointcount;k++){
	        for(int i=1;i<=pointcount;i++){
	            for(int j=1;j<=pointcount;j++){
	            	con[i][j]|=(con[i][k]&&con[k][j]);
	            }
	        }
		}
		/*   匈牙利算法       */
		//开始统计匹配度
		//初始化工具
		matchtable=new int[pointcount+1];
		
		int count=0;
		//统计匹配度
		//查看有哪些点可以匹配
		for(int i=1;i<=pointcount;i++) {
			ismatch=new boolean[pointcount+1];//每次都初始化一次
			if(dfs(i)) count++;
		}
		//最小路径覆盖=原图的结点数- 所有点根据con找到的最大匹配数
		System.out.println(pointcount-count);
		
		
	}

	/*  匈牙利算法核心        */
	//点a是否能够匹配到一个合适的点b,
	//判断标准就是con中两个点是否能连接
	//一个点可能会有很多个点连接
	//匈奴利亚算法的功能就是达到最大匹配度
	public boolean dfs(int i) {
		for(int j=1;j<=pointcount;j++) {
			if(!con[i][j])continue;//i和j无连接,跳过
			if(!ismatch[j]) {
				ismatch[j]=true;
				/* 如果在点i之前没有点与点j匹配,直接返回true,匹配成功
				 * 如果点j已有匹配的点k,
				 * 但点k还能找到其他点匹配。
				 * 那就赶走k点,取而代之,将点i与点j匹配
				 * 点k继续上述过程。
				 * 如果k的结果返回true。点k找到新的,说明点i可以与点j匹配,匹配成功
				 * 如果k的结果返回false,说明点i挤走点k,点k无法找到可以匹配的其他点
				 * 那对我们来说,匹配度是不变的,直接结束,不让i代替k。
				 */
				if(matchtable[j]==0||dfs(matchtable[j])) {
					matchtable[j]=i;
					return true;
				}
			}	
		}
		return false;
	}


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

 

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值