估计人数【第十届】【决赛】

给定一个NxM的方格矩阵,矩阵中每个方格标记 0或者 1代表这个方格
是不是有人踩过。
己知一个人可能从任意方格开始,之后每一
-步只能向右或者向下走一格。
走了若干步之后,这个人可以离开矩阵。这个人经过的方格都会被标记为 1,
包括开始和结束的方格。注意开始和结束的方格不需要一定在矩阵边缘。
请你计算至少有多少人在矩阵上走过。
【输入格式】
输入第一行包含两个整数 N、M。
以下N行每行包含 M 个整数 (0/1),代表方格矩阵。
【输出格式】
输出一个整数代表答案。
【样例输入】

5 5
00100
11111
00100
11111
00100

【样例输出】

3

本题思路

本题主要需要用到

匈牙利算法 可参考理解:趣写算法系列之--匈牙利算法_Dark_Scope的博客-CSDN博客_匈牙利算法

还需要了解最小路径覆盖的基本原理最小路径覆盖_Bowen_Yang的博客-CSDN博客_最小路径覆盖 

有了上面两个算法思路,那么这题就是一个经过变形的,最小路径覆盖问题

首先第一步题目要求只能向下或向右移动,那么我们就需要对输入数据进行判断,也就是能否向下和向右走,如果可以就进行标记,那么问题又来了,我咋判断这个和我要走的那个具体是哪一个,那么我们在输入时,就要对每个走过的点也就是标记为1的点进行标记,用sum记录我们一共用了多少个点,从1开始,然后我们在判断能否连通时,如果可以就在取出他们是第几个点,然后标记(注意这个标记是单向的)

然后,就是用Floyd,进行把有相交的变成不相交路径覆盖,这样就可以直接套匈牙利算法

然后通过结论

最小路径覆盖=原图的结点数-新图的最大匹配数

就可以解决这题了

本题用到大量图论的知识,还是挺难的

下面上代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;

public class Main估计人数 {
	static boolean bb[][],bj[];
	static int aa[][],m,vo[],sum;
	public static void main(String[] args) throws IOException {
		BufferedReader x=new BufferedReader(new InputStreamReader(System.in));
		PrintWriter out=new PrintWriter(System.out);
		String ss[]=x.readLine().split(" ");
		int n=Integer.valueOf(ss[0]);
		m=Integer.valueOf(ss[1]);
		sum=1;//统计有多少点需要走
		aa=new int[n][m];
		for(int i=0;i<n;i++) {
			String s=x.readLine();
			for(int j=0;j<m;j++)
				if(s.charAt(j)-48==1) {
					aa[i][j]=sum++;
				}
		}
		bb=new boolean[sum][sum];//用来记录是否可以到达
		vo=new int[sum];//用来标记和谁匹配
		bj=new boolean[sum];//用来标记是否到达过
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++) {
				if(aa[i][j]>0&&i+1<n&&aa[i+1][j]>0)//代表可以向下连通
					bb[aa[i][j]][aa[i+1][j]]=true;
				if(aa[i][j]>0&&j+1<m&&aa[i][j+1]>0)//代表可以向右连通
					bb[aa[i][j]][aa[i][j+1]]=true;
			}
		//转化成无相交的路径覆盖
		for(int i=1;i<sum;i++)
			for(int j=1;j<sum;j++)
				for(int k=1;k<sum;k++)
					if(bb[i][k]&&bb[k][j])//如果有传递性,直接加边这样就转化成不相交的情况,直接跳过中间点
						bb[i][j]=true;
		int kk=0;
		for(int i=1;i<sum;i++) {//遍历每个点
			Arrays.fill(bj, false);
			if(dfs(i))//如果可以成功匹配就加一
				kk++;
		}
		out.println(sum-kk-1);
		out.flush();
	}
	public static boolean dfs(int i) {//匈牙利方法求是否可以成功匹配
		for(int j=1;j<sum;j++) {
			if(!bb[i][j]||bj[j])//如果这个点被用过,或者无法到达
				continue;
			bj[j]=true;//标记
			if(vo[j]==0||dfs(vo[j])) {//如果可以成功插入
				vo[j]=i;
				return true;
			}
		}
		return false;
	}
}

如有侵权,联系删

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值