【每日蓝桥】51、一七年省赛Java组真题“分巧克力”

你好呀,我是灰小猿,一个超会写bug的程序猿!

欢迎大家关注我的专栏“每日蓝桥”,该专栏的主要作用是和大家分享近几年蓝桥杯省赛及决赛等真题,解析其中存在的算法思想、数据结构等内容,帮助大家学习到更多的知识和技术!

标题:分巧克力

儿童节那天有 K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。

小明一共有 N块巧克力,其中第 i 块是 Hi×Wi的方格组成的长方形。为了公平起见,小明需要从这 N块巧克力中切出 K块巧克力分给小朋友们。

切出的巧克力需要满足:

  1. 形状是正方形,边长是整数
  2. 大小相同

例如一块 6×5的巧克力可以切出 6 块 2×2 的巧克力或者 2 块 3×3

的巧克力。当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?

输入格式

第一行包含两个整数 NK

以下 N行每行包含两个整数 Hi 和 Wi

输入保证每位小朋友至少能获得一块 1×1的巧克力。

输出格式

输出切出的正方形巧克力最大可能的边长。

数据范围

1N,K≤10的5次方

1Hi,Wi≤10的5次方

输入样例:

2 10

6 5

5 6

输出样例:

2

【资源约定】

峰值内存消耗(含虚拟机) < 256M

CPU消耗< 1000ms

请严格按要求输出,不要画蛇添足地打印类似: " 请您输人..”的多余内容.

所有代码放在同一个源文件中,调试通过后,拷贝提交该源码.

不要使用package语句。不要使用jdk1.7及以上版本的特性.

主类的名字必须是: Main, 否则按无效代码处理.

提交程序时,注意选择所期望的语言类型和编译器类型.

解题思路:

本题在求解的时候有两种方法,一种是我们最容易想到的暴力枚举的方法,从边长为1的巧克力开始枚举,看所有的巧克力能不能切出k块出来,但是要注意的是这种方法对于输入简单的数据是行得通的,但是对于题中所说,1≤N,K≤10的5次方,1≤Hi,Wi≤10的5次方,当数据过大的时候,该方法所消耗的时间就会过大。

因此对于这种在已经排好序的大量的数据中进行查找的题型,最方便快捷的方法是使用二分查找。来表示所有可能的边长,通过二分查找规定一个左端是1,右端长度是所有边中最长边,然后开始对中间的数据mid=(l+r)/2进行判断,

如果以mid为边长切的巧克力数大于等于K,则说明边长还可以增加,这时让l=mid+1,继续判断。

如果以mid为边长切的巧克力数小于K,则说明该边长已经不能满足要求,这时让r=mid-1,继续判断,

直到最后l和r交叉结束。

答案源码:

暴力枚举法:

package 一七年省赛真题;

import java.util.Scanner;

public class Year2017_Bt9 {

	
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int N = scanner.nextInt();
		int K = scanner.nextInt();
		int [][] HW = new int[N][2];
		int maxBrim = 0;	//确定边的最大可能
		int sumArea = 0;	//记录所有巧克力的总面积
		int ans = 1;		//最终巧克力边长
		for (int i = 0; i < N; i++) {
			HW[i][0] = scanner.nextInt();
			HW[i][1] = scanner.nextInt();
			maxBrim = Math.max(maxBrim, Math.max(HW[i][0], HW[i][1]));
			sumArea += HW[i][0]*HW[i][1];
		}
//		System.out.println(maxBrim + " " + sumArea);
		for (int brim = 1; brim <= maxBrim; brim++) {
			int cklSum = 0;//记录当前边长的巧克力可以切多少个
			//如果分给小伙伴的巧克力的总面积小于等于当前拥有巧克力的总面积
			if (brim*brim*K<=sumArea) {
				//遍历N块巧克力
				for (int i = 0; i < N; i++) {
					 cklSum += getCount(HW[i][0],HW[i][1],brim);
//					 System.out.println("当前巧克力:" + cklSum + " 边长:" + brim);
					 //如果切的巧克力的个数大于等于同学人数
					 if (cklSum>=K) {
						ans=brim;
						break;
					 }
				}
				//如果随着边长的增加,巧克力数目小于了同学人数,就不再遍历
				if (cklSum<K) {
					break;
				}
				
			}else {
				break;
			}
		}
		
		System.out.println(ans);
	}

	/**
	 * 计算高为h,宽为w的巧克力可以切多少个边长为brim的巧克力
	 * */
	private static int getCount(int h, int w, int brim) {
		int maxB = Math.max(h, w);	//最长边
		int minB = Math.min(h, w);	//最短边
		//如果最短边大于巧克力边长,那么至少可以切一个
		if (brim<=minB) {
			int x = maxB/brim;	//长边可以切巧克力的个数
			int y = minB/brim;	//短边可以切巧克力的个数
			return x*y;		//该块巧克力可以切的边长为brim的巧克力的个数
		}else {
			return 0;//否则一个也切不了
		}
	}

}

二分查找法:

package 一七年省赛真题;

import java.util.Scanner;

public class Year2017_Bt9_2 {

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int N = scanner.nextInt();
		int K = scanner.nextInt();
		int [][] HW = new int[N][2];
		int maxBrim = 0;	//确定边的最大可能
		int ans = 1;		//最终巧克力边长
		for (int i = 0; i < N; i++) {
			HW[i][0] = scanner.nextInt();
			HW[i][1] = scanner.nextInt();
			maxBrim = Math.max(maxBrim, Math.max(HW[i][0], HW[i][1]));
		}
		
		int l = 1;
		int r = maxBrim;
		while (l<=r) {
			int mid = (l+r)/2;
			int cklSum = 0;
			//遍历N块巧克力
			for (int i = 0; i < N; i++) {
				 cklSum += (HW[i][0]/mid)*(HW[i][1]/mid);
//				 System.out.println("当前巧克力:" + cklSum + " 边长:" + brim);
				 //如果切的巧克力的个数大于等于同学人数
				 if (cklSum>=K) {
					ans=mid;
					l = mid+1;
					break;
				 }
			}
			//如果所有巧克力都切完后,切的巧克力的个数小于于等于同学人数
			//最右端向左移
			if (cklSum<K) {
				r = mid-1;
			}
		}
		System.out.println(ans);
	}

}

输出样例:

 

其中有不足或者改进的地方,还希望小伙伴留言提出,一起学习!

感兴趣的小伙伴可以关注专栏!

灰小猿陪你一起进步!

猜你喜欢

转载自blog.csdn.net/weixin_44985880/article/details/115407557