蓝桥杯:分巧克力(C++)

1、问题描述
儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。
为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:
1. 形状是正方形,边长是整数
2. 大小相同
例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。

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

输入

第一行包含两个整数N和K。(1 <= N, K <= 100000)

以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)

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

输出

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

样例输入:

2 10

6 5

扫描二维码关注公众号,回复: 10243451 查看本文章

5 6

样例输出:

2

资源约定:

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

CPU消耗 < 1000ms

2、我对这个问题的看法,理解
  在我看来,这个问题的本质是如何把N块巧克力切出K块最大的面积相等的正方形。即可以把转化为动态规划问题:让正方形的边长在一定的范围内动态变化,在众多情况中找到那个符合条件的最优情况。

3、我的解法思想
(1)首先,定义一些代码所需的全局变量,如整型数组Hi[100000]、Wi[100000]、number[100000],整型变量sum。
(2)接着,写一些功能函数,如min()函数、countSum()函数、searchMaxshortside() 函数。
(3)然后,写关键函数cutting()函数,再cutting()函数中调用上面的功能函数,实现找最大值功能。在下一部分有详细的函数说明
(4)最后,写main()函数,调用cutting()函数即可。

4、关键函数cutting()详解
切割函数:cutting()函数

函数功能:模拟切割巧克力,找到正方形边长最大的情况,并返回最大的正方形边长。

题目要求:每位小朋友都有一样的尽可能大的巧克力。
翻译成代码为:countSum(n)>=k。其中countSum(n)是切割出的正方形的个数,k为小朋友人数。

函数的算法思想:
(1)首先,完成变量的定义,巧克力块数N、小朋友人数K以及每块巧克力长Hi和宽Wi的输入。

(2)接着,调用searchMaxshortside()函数,查找所有的巧克力的最大的短边值maxShortside,并赋值给maxShortside作为循环边界;

(3)接着,用两层for循环实现模拟切割巧克力的动态过程。
具体的做法:
  外层for循环把上一步得到的最大的短边值maxShortside作为控制执行次数的条件,即能切割的正方形最大边长取决于所有巧克力长和宽中的最大的短边值maxShortside,所以把maxShortside作为边界条件。每执行完一次内循环,把得到的countSum(n)和k比较,判断是否符合条件,若符合条件,就把此时的正方形边长i存放到maxLength中,再把边长i加1去模拟下一种情况。
  内存for循环用于计算在边长确定的情况下,统计所有巧克力可以切出的总个数sum。(用数组number[ ]存放每块巧克力可以切出的个数)
一块巧克力能切出的正方形个数计算公式:
  正方形个数=(长/边长)×(宽/边长);
注:正方形个数可以为0,即该块巧克力不切

(4)最后,返回符合条件的最大正方形边长maxLength;

注意点:
  在每次外循环执行后,要把计数因子sum置0,否则会影响下一次的判断。

函数cutting()具体代码

int cutting(int n,int k)           //切割函数; 
{
	int maxLength=1,maxShortside;     //maxLength为满足条件的最大边长,maxShortside为所有的Hi和Wi中最大的短边值; 
	cin>>n>>k;                     //输入巧克力的块数和小朋友的人数; 
	for(int i=0;i<n;i++)
	{
		cin>>Hi[i]>>Wi[i];         //依次输入每块巧克力的长Hi和宽Wi; 
	}	
	maxShortside=searchMaxshortside(n);   //查找所有的Hi和Wi中寻找最大的短边值,并赋值给maxShortside; 
	for(int i=1;i<=maxShortside;i++) //控制切割出的正方形的边长,控制条件:边长<=maxShortside; 	                               
	{                              //原因:因为所有的正方形一样大,最大的短边值就限制了可以切出的正方形的边长; 
		sum=0;                      //记录每种边长切出的正方形巧克力的块数; 注意:在每次判断后要把sum的值置0,不然结果会出错; 
		for(int j=0;j<n;j++)
		{
			number[j]=(Hi[j]/i)*(Wi[j]/i);   //一块巧克力能切出的正方形个数=(长/边长)×(宽/边长); 注意:巧克力可以不切; 
		}                                   //原因:长÷边长可以得到一个小于等于长的整数,同理,宽也一样,二者乘积为正方形块数(可以为0); 
		if(countSum(n)>=k)             //条件判断:当切出的正方形块数大于小朋友人数时,边长符合条件; 
		{
			maxLength=i;               //记录符合条件的边长,开始下一次循环;若边长符合条件,就覆盖上一个边长,直至循环结束; 
		}
	}
	cout<<maxLength;                 //循环结束,返回符合条件的最大边长; 
}

5、解法完整代码

#include<iostream>
using namespace std;
int Hi[100000],Wi[100000];           //定义存放每块巧克力长和宽的数组Hi[],Wi[]; 
int number[100000],sum=0;            //定义存放每块巧克力被切出的正方形个数的数组number[]和存放每种边长对应的总个数的变量sum; 

int min(int a,int b)                 //求两数中的最小值,用于求每块巧克力的Hi和Wi中的最小值; 
{
	if(a<b) return a;
	else return b;
}

int countSum(int n)                  //计算每种边长对应的正方形总个数sum; 
{
	for(int i=0;i<n;i++)
	{
		sum+=number[i];
	}
	return sum;
} 

int searchMaxshortside(int n)      //在所有的Hi和Wi中寻找最大的短边值maxshortside,因为要把maxshortside作为控制循环结束的条件; 
{
	int maxshortside;                
	int minL[100000];             //数组minL[]是存放每对Hi和Wi中最小值数组; 
	for(int i=0;i<n;i++)
	{
		minL[i]=min(Hi[i],Wi[i]);    
	} 
	maxshortside=minL[0];      //把数组minL[]的首元素赋给maxshortside作为初值,开始寻找在所有的Hi和Wi中寻找最大的短边值maxshortside; 
	for(int i=1;i<n;i++)
	{
		if(minL[i]>maxshortside)
			maxshortside=minL[i];
	}
	return maxshortside;            //返回最大的短边值maxshortside; 
}

int cutting(int n,int k)           //切割函数; 
{
	int maxLength=1,maxShortside;     //maxLength为满足条件的最大边长,maxShortside为所有的Hi和Wi中最大的短边值; 
	cin>>n>>k;                     //输入巧克力的块数和小朋友的人数; 
	for(int i=0;i<n;i++)
	{
		cin>>Hi[i]>>Wi[i];         //依次输入每块巧克力的长Hi和宽Wi; 
	}	
	maxShortside=searchMaxshortside(n);   //查找所有的Hi和Wi中寻找最大的短边值,并赋值给maxShortside; 
	for(int i=1;i<=maxShortside;i++) //控制切割出的正方形的边长,控制条件:边长<=maxShortside; 	                               
	{                              //原因:因为所有的正方形一样大,最大的短边值就限制了可以切出的正方形的边长; 
		sum=0;                      //记录每种边长切出的正方形巧克力的块数; 注意:在每次判断后要把sum的值置0,不然结果会出错; 
		for(int j=0;j<n;j++)
		{
			number[j]=(Hi[j]/i)*(Wi[j]/i);   //一块巧克力能切出的正方形个数=(长/边长)×(宽/边长); 注意:巧克力可以不切; 
		}                                   //原因:长÷边长可以得到一个小于等于长的整数,同理,宽也一样,二者乘积为正方形块数(可以为0); 
		if(countSum(n)>=k)             //条件判断:当切出的正方形块数大于小朋友人数时,边长符合条件; 
		{
			maxLength=i;               //记录符合条件的边长,开始下一次循环;若边长符合条件,就覆盖上一个边长,直至循环结束; 
		}
	}
	cout<<maxLength;                 //循环结束,返回符合条件的最大边长; 
}
	
int main()
{
	int N,K;
	cutting(N,K);
	return 0;
}

运行环境:DEV C++

6、运行截图
巧克力块数N=10,小朋友人数K=10。
 第一块巧克力长4,宽7;
 第二块巧克力长8,宽6;
 第二块巧克力长4,宽6;
 第二块巧克力长7,宽3;
 第二块巧克力长10,宽2;
 第二块巧克力长3,宽8;
 第二块巧克力长1,宽10;
 第二块巧克力长4,宽7;
 第二块巧克力长1,宽7;
 第二块巧克力长3,宽7;

截图:
在这里插入图片描述
至此,整个题目解答完毕!!!

结语:以上就是我对这个问题的理解、解法,可能存在着更好、更简洁的解法代码,希望大家提出来,我们一起讨论,交换看法,共同进步。若上述代码中存在问题,望大家指正,谢谢大家看到结尾。(∩^∩)

奋斗的2351

发布了12 篇原创文章 · 获赞 3 · 访问量 390

猜你喜欢

转载自blog.csdn.net/weixin_45620022/article/details/104919872