给定一个NxM的矩阵A和一个整数K,小Hi希望你能求出其中最大(元素数目最多)的子矩阵,并且该子矩阵中所有元素的和不超过K。

21 篇文章 0 订阅

最大子矩阵

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述
给定一个NxM的矩阵A和一个整数K,小Hi希望你能求出其中最大(元素数目最多)的子矩阵,
并且该子矩阵中所有元素的和不超过K。
输入
第一行包含三个整数N、M和K。
以下N行每行包含M个整数,表示A。
对于40%的数据,1 <= N, M <= 10
对于100%的数据,1 <= N, M <= 250 ; 1 <= K <= 2147483647 ; 1 <= Aij <= 10000
输出
满足条件最大的子矩阵所包含的元素数目。如果没有子矩阵满足条件,输出-1。
样例输入

3 3 9
1 2 3
2 3 4
3 4 5

样例输出

4

思路:
首先先画个图
这个只画了从0.0开始的子矩阵
在这里插入图片描述
我的思路是首先通过排序
让一维排序有序
再让一维数组中最小元素来排列二维数组
这样实现从左上递增到做小
然后通过一个辅助同样大小的数组
让每个位置都成为0.0到该位置矩阵的元素的和

在这里插入图片描述
红色位置就是矩阵的和
也就是说可以在求和的过程中,判断是否大于k
大于k了那就跳出

//数据量:1 <= N, M <= 250 ; 1 <= K <= 2147483647 ; 1 <= Aij <= 10000
//时间复杂度:O(N^M)
public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		int M = sc.nextInt();
		long K = sc.nextLong();
		long[][] arr = new long[N][M];
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < M; j++) {
				arr[i][j] = sc.nextLong();
			}
			Arrays.sort(arr[i]);//N*NlogN
		}
		long now = System.currentTimeMillis();
		sort(arr,0,N-1);//NlogN
		//排序之后左上的节点不需要动
		//左上最小,右小最大
		//左边比右边小
		long maxCount = 0;
		long[][] help = new long[N][M];
		l1:for (int i = 0; i < N; i++) {//N^M
			for (int j = 0; j < M; j++) {
				if(i==0&&j==0) {//第一个元素是为本身
					help[i][j] = arr[i][j];
				}
				else if(i==0 && j!=0) {//第一行
					help[i][j] = arr[i][j] + help[i][j-1];
				}
				else if(j==0 && i!=0) {//第一列
					help[i][j] = arr[i][j] + help[i-1][j];
				}
				else {
					//后面为上右相加这样当前位置就是0.0到当前的总和
					help[i][j] = arr[i][j] + help[i-1][j] + help[i][j-1]-help[i-1][j-1];
					//一定要减去他左上一个对角位置的值,因为上元素的和包括左上角值,左元素也包括左上角值,那么就重复了
					//这个地方一定要验算最后一个元素的和是不是整个矩阵的和
				}
				int t = (i+1)*(j+1);//每次都把元素个数统计
				if(help[i][j] > K) {//大于了k跳出
					break l1;	
				}
				else if(maxCount<t) {//如果当前的元素大于最大的计数那么赋值给他
					maxCount = t;
				}
			}
		}
		System.out.println(maxCount);
		System.out.println(System.currentTimeMillis()-now+"ms");
	}
		
	//根据二维数组第一个元素排序
	static void sort(long[][] arr,int begin,int end) {
		if(begin < end) {
			if (end-begin+1<7) {//排序元素小于7改插入排序
				for (int i = begin+1; i <= end; i++) {
					long val = arr[i][0];
					long[] t = arr[i];
					int index = i - 1;
					while(index > -1 && val < arr[index][0]) {
						arr[index+1] = arr[index];
						index--;
					}
					arr[index+1] = t;
				}
			}else {
				int p = Mid(arr,begin,end);
				sort(arr, begin, p-1);
				sort(arr, p+1, end);
			}
			
		}
	}
	static int Mid(long[][] arr, int begin, int end) {
		//三点优化
		int mid = (begin + end) >>1;
		if (arr[begin][0] > arr[mid][0] && arr[begin][0] < arr[end][0]) {
			mid = begin;
		}else if(arr[end][0] > arr[begin][0] && arr[end][0] < arr[mid][0]) {
			mid = end;
		}
		long[] t = arr[begin];arr[begin] = arr[mid];arr[mid] = t;
		
		long poivt = arr[begin][0];
		int left = begin+1;
		int right = end;
		while(left<=right) {
			if(left<=right && arr[left][0]<=poivt)left++;
			if(left<=right && arr[right][0]>poivt)right--;
			if(left<right) {
				t = arr[left];arr[left] = arr[right];arr[right] = t;
			}
		}
		t = arr[begin];arr[begin] = arr[right];arr[right] = t;
		return right;
	}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值