网易面试题之分田地

分田地

题目描述

牛牛和 15 个朋友来玩打土豪分田地的游戏,牛牛决定让你来分田地,地主的田地可以看成是一个矩形,每个位置有一个价值。分割田地的方法是横竖各切三刀,分成 16 份,作为领导干部,牛牛总是会选择其中总价值最小的一份田地, 作为牛牛最好的朋友,你希望牛牛取得的田地的价值和尽可能大,你知道这个值最大可以是多少吗?

输入描述:

每个输入包含 1 个测试用例。每个测试用例的第一行包含两个整数 n 和 m(1 <= n, m <= 75),表示田地的大小,接下来的 n 行,每行包含 m 个 0-9 之间的数字,表示每块位置的价值。

输出描述:

输出一行表示牛牛所能取得的最大的价值。

示例1

输入

复制

4 4
3332
3233
3332
2323

输出

复制

2

算法思路

难点:

暴力化简:枚举+二分,纯暴力会导致时间超限。

理解题意:将我们的矩阵划分成16部分,对于其中的划分矩阵a,求解一个矩阵a,使得在所有的枚举 a1,a2,a2....an中,ai的划分的16部分中的最小部分min,在所有的min(a1.....an)中是最大的。也就是说我们要找到在所有划分中权值最小的部分中的最大值,即max(min(a[0]),min(a[1]),....min(a[k]))。

此题目属于暴力美学中的化简,枚举+二分。

纯暴力:如果我们采用纯暴力的方式,对于每一刀都进行枚举,那么时间复杂度将达到 O(n^3m^3),很明显如此高的复杂度会导致时间超限。

枚举+二分:从宏观的角度看,令我们所有土地的权值和为r,那么我们的最小值resualt一定在(0,r)的闭区间之中,原因是我们不可能找到一个划分,使得其中的一块土地等于0或者说土地权值=r。有了如上思路,那么我们就可以来进行求解了。

1、首先我们来获得sum[i][j],代表从整个田地的左上角,到以land[i][j]土地为右下角的矩形土地权值之和。

2、接下来我们对 0 - sum[n][m]值来进行二分查找,寻找我们满足条件的最大mid。

3、如果说我们的mid为我们所找的土地和,那么根据题意,在划分的16个部分中,我们的mid一定是最小的,如果存在一部分要小于我们的mid,那么我们的这种切法是不满足该条件的,应该重新来切。

4、为了减少复杂度,我们枚举所有的竖切,然后来进行我们的横切,每切一刀我们判断产生的四个矩阵的权值与mid的关系,如果满足均大于或等于我们的mid,那么我们在该基础上,在该横row的下放再寻找横切。

5、当我们找到了横切的四刀满足了16部分都大于等于我们的mid时,证明我们找到了一种切法,该切法一定满足存在一个划分,使得最小值大于或等于mid,于是我们返回true,更新我们的l边界值,重复我们的3,4。如果没有,则证明不存在一个划分,使得最小的一部分全部大于或等于mid,那么我们需要减小我们的mid值,重新进行查找。

6、最后我们输出我们的resualt值即可。

解题代码

import java.util.Scanner;
/**
 * 牛客网-划分田地
 * 枚举+二分查找
 * @author yangjieyu
 *
 */
public class Main {
	//土地
	static int[][] lands=new int[76][76];
	static int[][] sum=new int[76][76];
	static int n;
	static int m;
	public static void main(String[] args) {
		Scanner scanner=new Scanner(System.in);
		 n=scanner.nextInt();
		 m=scanner.nextInt();
		for(int i=1;i<=n;i++){
			String line=scanner.next();
			for(int j=1;j<=m;j++){
				lands[i][j]=line.charAt(j-1)-'0';
				//从 左上角sum[0][0]到sum[i][j]的土地价值之和
				sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+lands[i][j];
			}
		}
		int result=0;
		// r是土地总和
		int l=0,r=sum[n][m];
		//二分查找
		while(l<=r) {
			int mid = (r+l)>>1;
			if(judge(mid)) {
			//能够找到一个土地使得mid是最小值
			//增大mid的值继续查找
				result = mid;
				l  = mid+1;
			}else {
			//不能找到一个土地是得mid不是最小值
			//做查找mid
				r = mid -1;
			}
		}
		System.out.println(result);
		scanner.close();
	}
	
	public static boolean judge(int mid){
		//遍历我们的竖切
		for(int i = 1 ;i<=m-3;i++) {
			for(int j = i+1;j<=m-2;j++) {
				for(int k = j+1;k<=m-1;k++) {
					//每一刀切的行数的前一刀的位置,初始刀位置为0,也就是我们的边界
					int startRow = 0;
					//横切的刀数
					int count = 0;
					for(int r = 1;r<=n;r++) {
						int s1 = getCol(r, i, startRow, 0);
						int s2 = getCol(r, j, startRow, i);
						int s3 = getCol(r, k, startRow, j);
						int s4 = getCol(r, m, startRow, k);
						//最大值中的最小值
						if(s1>=mid&&s2>=mid&&s3>=mid&&s4>=mid) {
							startRow = r;
							count++;
						}
					}
					//如果该mid土地情况下找到了我们的最小值,则可以直接返回,不需要再遍历了
					if(count>=4) {
						return true;
					}
				}
			}
		}
		//不存在所有的划分中土地小于mid,我们需要减小mid的值
		return false;
	}
	//获得划分土地的大小
	public static int getCol(int x ,int y,int i,int j) {
		return sum[x][y] - sum[x][j]-sum[i][y]+sum[i][j];
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值