百錢買百雞(列舉思想編寫,並進行3次優化)

日子總要往前走發表於2020-12-31

1.題目

我國古代數學家張丘建在《算經》一書中曾提出著名的“百錢買百雞”問題:雞翁一,值錢五;雞母一,值錢三;雞雛三,值錢一;百錢買百雞,則翁、母、雛各幾何?
翻譯過來,意思是公雞一個五塊錢,母雞一個三塊錢,小雞三個一塊錢,問公雞、母雞、小雞各多少隻?

2.列舉法思想解法

2.1 列舉法思路

  • 列舉變數:公雞,母雞,小雞對應了i,j,k
  • 列舉範圍:公雞,母雞,小雞都是1-100次,對應了i,j,k分別迴圈一百次
  • 列舉判斷條件:
    • 錢數=100:5公雞+3母雞+1/3小雞=100 對應 5i+3j+k/3 == 100
    • 總雞數=00:公雞+母雞+小雞=100 對應 i+j+k == 100
    • 小雞必須為整數:小雞%3=0 對應 k%3 == 0
#include<iostream>
using namespace std;
int main()
{
	int count = 0;
	for(int i = 1 ; i <= 100 ; i++)
	{
		for(int j = 1 ; j <= 100 ; j++)
		{
			for(int k = 1 ; k <= 100 ; k++)
			{
				count++;
				if(k%3 == 0 && 5*i+3*j+k/3 == 100 && i+j+k == 100){
					cout<<"雞翁"<<i<<"只,雞母"<<j<<"只,雞雛"<<k<<"%d只"<<endl;
				}
			}
		}
	}
	cout<<"迴圈次數="<<count<<endl;
	return 0;
}

看得出來總迴圈次數是1000000次

3.第一次優化:縮小列舉範圍

  • 因為一百塊錢總共可以買20只公雞,且母雞和小雞最少一隻,因此公雞的取值範圍可以縮小為1-18也就是1≤i≤18
  • 因為一百塊錢最多可以買34只母雞(33.33),且公雞和小雞最少一隻,因此母雞的取值範圍為1≤j≤32
  • 因為小雞一元三隻,所以這裡無所縮小,公雞和母雞最少一隻,因此小雞的取值範圍為1-98,也就是1≤k≤98
#include<iostream>
using namespace std;
int main()
{
	int count = 0;
	for(int i = 1 ; i <= 18 ; i++)
	{
		for(int j = 1 ; j <= 32 ; j++)
		{
			for(int k =1 ; k <= 98 ; k++ )
			{
				count++;
				if(k%3 == 0 && 5*i+3*j+k/3 == 100 && i+j+k==100){
					cout<<"雞翁"<<i<<"只,雞母"<<j<<"只,雞雛"<<k<<"只"<<endl;
				}
			}
		}
	}
	cout<<"迴圈次數="<<count<<endl;
	return 0;
}

看得出來總迴圈次數是56448次

4.第二次優化:減少列舉變數

  • 因為公雞,母雞和小雞的總數是100,所當公雞和母雞確定的時候,小雞就可以用總數減去公雞,減去母雞來表示也就是k=100-i-j;
#include<iostream>
using namespace std;
int main()
{
	int count = 0;
	for(int i = 1 ; i <= 18 ; i++)
	{
		for(int j = 1 ; j <= 32 ; j++)
		{
			count++;
			int k = 100 - i - j;
			if(k%3 == 0 && 5*i+3*j+k/3 == 100){
				cout<<"雞翁"<<i<<"只,雞母"<<j<<"只,雞雛"<<k<<"只"<<endl;
			}
		}
	}
	cout<<"迴圈次數="<<count<<endl;
	return 0;
}

看得出來總迴圈的次數是576

5.第三次優化:進一步減少列舉變數

  • 先來看三個列舉變數之間的關係
    • i+j+k=100
    • 5i+3j+k/3=100
      利用上述二式,消去k得14i+8j=200–>7i+4j=100
      j = (100-7i)/4
      k = 100 - (100-7i)/4
  • 從7i+4j=100可以推出j最小取1,則4j為4,7i最大為96,故i的取值範圍為1-13
#include<iostream>
using namespace std;
int main()
{
	int count = 0;
	for(int i = 1 ; i <= 13 ; i++)
	{
		count++;
		int j = (100-7*i)/4;
		int k = 100 - j -i;
		if( (100-7*i)%4 == 0  && k%3 == 0){
			cout<<"雞翁"<<i<<"只,雞母"<<j<<"只,雞雛"<<k<<"只"<<endl;
		}
	}
	cout<<"迴圈次數="<<count<<endl;
	return 0;
}

看得出來總迴圈次數減少到了13
大家可以看得出來列舉法的總體思路是比較簡單的並不複雜,所以我們需要儘可能的去優化它。從最開始的1000000優化到最後的13次,可以看出優化的作用,更可以從中體會到演算法的魅力!

4個cpp原始檔下載:點選下載
其實沒必要下載,自己從程式碼框中複製就可以了

相關文章