藍橋杯:分巧克力(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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章