藍橋杯——暴力列舉篇

時間最考驗人發表於2021-10-13

暴力列舉

本學期學校開設了C++程式競賽的課程,每週雖然只有一節課,但老師講的是真的精彩,建模到對映等等思維的飛躍,還有各種excel、word解題大法,讓我歎為觀止!總結一些例題,希望能對自己以及想要參加藍橋杯的你提供些許幫助!(例題、解題方法等往後會不斷的更新總結!)

蠻力法:嘗試每一種可能

【暴力解法】

  • 確定範圍
  • 找出條件
  • 明確輸出的個數、型別、次序、格式

一般找數的題目都可以用暴力列舉

範圍精確好的好——有些條件可直接放進在範圍中

例題1、2013藍橋杯——猜年齡

【題目描述】

美國數學家維納(N.Wiener)智力早熟,11歲就上了大學。他曾在1935~1936年應邀來中國清華大學講學。
一次,他參加某個重要會議,年輕的臉孔引人注目。於是有人詢問他的年齡,他回答說:
“我年齡的立方是個4位數。我年齡的4次方是個6位數。這10個數字正好包含了從0到9這10個數字,每個都恰好出現1次。”
請你推算一下,他當時到底有多年輕。

解法一:excel

思路:

利用excel中的=POWER()函式分別計算出1~32的立方和四次方,根據條件:這10個數字正好包含了從0到9這10個數字,每個都恰好出現1次即找到18

image

【解法二】

思路:

  • 正常人的年齡一般0到100歲。由題意:年齡的立方是個4位數。年齡的4次方是個6位數——通過計算器可初步確認範圍10到32歲之間

  • 用a[10]陣列來計數:記錄4位數和6位數每一位中數字0~9出現的次數

  • 如果 a[i]大於1則證明重複 。a[i] == 10 則成立

#include<iostream>
#include<cmath>

using namespace std;

void check(int num, int a[])
{
    while(num != 0)
    {
        
        a[num % 10] ++;
        num = num / 10;
    }
}
 
int main()
{   
    int a[10] = {0};
    int num1, num2, count = 0;
    for(int x = 10; x <= 32; x++)
    {
        num1 = x * x * x;
        if(num1 >= 1000 && num1 <= 9999){
            num2 = num1 * x;
            if(num2 >= 100000 && num2 <= 999999)
            {
                check(num1, a);
                check(num2, a);
                for(int i = 0; i < 10; i++)
                {
                  if(a[i] == 1) count++;
                }              
            }
        }
       if(count == 10)
       {
           cout << x;
           break;
       }
    
    }
return 0;
}

【解法三】:STL容器set:資料不能出現重複;例如100個數中無重複——將這一百個數插入s集合中,最終集合的大小s.size()==100即表明沒有重複。

#include<iostream>
#include<cmath>
#include<set>

using namespace std;
int main()
{   
    set<int>s;
    int num1, num2;
    for(int x = 10; x <= 32; x++)
    {
        num1 = x*x*x;
        if(num1 >= 1000 && num1 <= 9999){
            num2 = num1 * x;
            if(num2 >= 100000 && num2 <= 999999)
            {
                while(num1 !=0)
                {
                    s.insert(num1 % 10);
                    num1 = num1 / 10;
                }
                
                while(num2 !=0)
                {
                    s.insert(num2 % 10);
                    num2 = num2 / 10;
                }
            }
        }
        
        if(s.size() == 10)
        {
            cout << x;
            break;
        }
    }
return 0;
}

【解法四】:

  • 將4位數和6位數(整數)轉為字串
  • 將兩個字串拼接(10位數)
  • 檢查是否重複(set)

注:引入標頭檔案:#include<set>#include<string>#include<sstream>

整數轉字串方法模板:

void i2s(int num, string & str)
{
    stringstream ss;
    ss << num;
    ss >> str;
}

【參考程式碼】

#include<iostream>
#include<cmath>
#include<set>
#include<string>
#include<sstream>

using namespace std;

void i2s(int num, string & str)
{
    stringstream ss;
    ss << num;
    ss >> str;
}
bool check(string str)
{   
    set<char>s;// 注意:此時set是char型別
    for(int i=0; i < str.length(); i++)
    {
        s.insert(str[i]);
    }
    if(str.length() == 10 && s.size() == 10) return true;
    return false;
}
int main()
{   
    
    int num1, num2;
    for(int x = 10; x <= 32; x++)
    {
        num1 = x*x*x;
        num2 = num1 * x;
        if(num1 >= 1000 && num1 <= 9999 && num2 >= 100000 && num2 <= 999999){
            // 將整數轉為字串
            string str1, str2;
            i2s(num1, str1);
            i2s(num2, str2);
            // 4位和6位 拼接後檢查
            if(check(str1 + str2))
            {
                cout << x;
            }
        }
            
    }
     
return 0;
}

例題2、藍橋杯——古堡算式

【問題描述】

福爾摩斯到某古堡探險,看到門上寫著一個奇怪的算式:
ABCDE * ? = EDCBA
他對華生說:“ABCDE應該代表不同的數字,問號也代表某個數字!”
華生:“我猜也是!”
於是,兩人沉默了好久,還是沒有算出合適的結果來。
請你利用計算機的優勢,找到破解的答案。
把 ABCDE 所代表的數字寫出來。
答案寫在“解答.txt”中,不要寫在這裡!

解法:

1、列舉五個數:範圍0~9

2、條件:ABCDE應該代表不同的數字 (五位數)

3、條件:ABCDE * ? = EDCBA——x*? = y --> y%x == --->得出x(五位數的才對)

#include<iostream>

using namespace std;

int main()
{   int x, y;
    for(int a = 0; a < 10; a++){
        for(int b = 0; b < 10 ; b++){
            if( b != a)
            for(int c = 0; c < 10; c++){
                if(  c!=a && c!=b)
                for(int d = 0; d < 10 ; d++){
                    if( d!=a && d!=b && d!=c)
                   for(int e = 0; e < 10  ; e++) {
                       if( e!=a && e!=b && e!=c && e!=d){
                        x = a*10000 + b*1000 + c*100 + d*10 + e;//ABCDE
                        y = e*10000 + d*1000 + c*100 + b*10 + a;//EDCBA
                        if(y % x == 0)
                        {
                            cout << x << endl;
                            break;
                        }

                      }
                   }
                }
            }
            
        }
    }
    
return 0;
}

輸出結果:

2178
4356
21978

例題3、藍橋杯——低碳生活大獎賽

【問題描述】

某電視臺舉辦了低碳生活大獎賽。題目的計分規則相當奇怪:

每位選手需要回答10個問題(其編號為1到10),越後面越有難度。答對的,當前分數翻倍;答錯了則扣掉與題號相同的分數(選手必須回答問題,不回答按錯誤處理)。

每位選手都有一個起步的分數為10分。

某獲勝選手最終得分剛好是100分,如果不讓你看比賽過程,你能推斷出他(她)哪個題目答對了,哪個題目答錯了嗎?

如果把答對的記為1,答錯的記為0,則10個題目的回答情況可以用僅含有1和0的串來表示。例如:0010110011 就是可能的情況。

你的任務是算出所有可能情況。每個答案佔一行。

思路:

  • 一看到答案所給例子想到用二進位制數求解
  • 遍歷判斷二進位制數1就當前分數翻倍;0就當前分數減當前題號所對應分數

由於沒搞清二進位制數與十進位制數範圍關係,一開始我遍歷的是0-255(2的八次方)(8位無符號二進位制數對應十進位制範圍)是一直沒有結果的。本題是10題即十位二進位制數:0~1024(2的10次方)

解題關鍵:要判斷二進位制數的每一位就顯得出十進位制數對應二進位制數的每一位(應用:二進位制數的第k位是幾?(可以看本人部落格位運算筆記))

例如:
	int x = 10;
    for(int i = 9; i >=0; i++)
    {
		cout << ( x >> k & 1);
    }
輸出結果:0 0 0 0 0 0 1 0 1 0 

【參考程式碼】

#include<iostream>
using namespace std;


int main(){


    int a[10]={10,9,8,7,6,5,4,3,2,1}; // 記錄錯誤題目對應的扣分
    int score = 10;
        for(int x = 0 ; x <= 1024; x++) // 遍歷所有無符號10位二進位制數對應的十進位制數
        {
            
            for(int i = 9; i >= 0; i --) // 將十進位制數轉為0、1二進位制數(左到右一位一位判斷)
            {

            if((x >> i & 1) == 1) score = 2 * score; 

                
            else if((x >> i & 1) == 0) score -= a[i];

            
            }
            
              if(score == 100)
              { 
                for(int j = 9; j >= 0; j --)
                cout<< (x >> j & 1)<<" ";
                cout << endl;
              }
              
             score = 10; // 因為score定義的是全域性變數,因此沒判斷完一個x就要更新
        }
    return 0;
}

輸出結果:

0 0 1 0 1 1 0 0 1 1 
0 1 1 1 0 1 0 0 0 0 
1 0 1 1 0 1 0 0 0 0 

例題4、藍橋杯——海盜喝酒

【問題描述】

有一群海盜(不多於20人),在船上比拼酒量。過程如下:開啟一瓶酒,所有在場的人平分喝下,有幾個人倒下了。再開啟一瓶酒平分,又有倒下的,再次重複… 直到開了第4瓶酒,坐著的已經所剩無幾,海盜船長也在其中。當第4瓶酒平分喝下後,大家都倒下了。
等船長醒來,發現海盜船擱淺了。他在航海日誌中寫到:“…昨天,我正好喝了一瓶…奉勸大家,開船不喝酒,喝酒別開船…”
請你根據這些資訊,推斷開始有多少人,每一輪喝下來還剩多少人沒倒下。
如果有多個可能的答案,請列出所有答案,每個答案佔一行。
格式是:人數,人數,…
例如,有一種可能是:20,5,4,2,0
多個答案排列順序不重要。

思路:所有人平分,直到開了第4瓶酒,海盜船長也在其中,我正好喝了一瓶

  • 範圍:(不多於20人):則第一輪至少為4人——a1:[4,20] a2:[3,a1) [2,a2) [1,a3)(後面三輪每一輪的人數都會比前一輪少,因為喝醉了),當然你直接1~20也可以

  • 條件:所有人平分,直到開了第4瓶酒,海盜船長也在其中,我正好喝了一瓶(1/a1 + 1/a2 + 1/a3 + 1/a4 == 1)

  • 輸出結果五個數,其中最後一個為0,即我們要求四個數(四輪每一輪的人數)

注:1/a1 + 1/a2 + 1/a3 + 1/a4 == 1這樣使用不是很規範/整除——因此要通分

【參考程式碼】

#include<iostream>
using namespace std;


int main(){

    for(int a1 = 4; a1 <= 20; a1++){
        for(int a2 = 3; a2 < a1; a2++){
          for(int a3 = 2; a3 < a2; a3++){
              for(int a4 = 1; a4 < a3; a4++){
                  if(a2*a3*a4 + a1*a3*a4 + a1*a2*a4 + a1*a2*a3 == a1*a2*a3*a4)
                  {
                      cout<< a1 <<" "<< a2 <<" " << a3 <<" "<< a4<<" "<< "0" <<endl;
                  }
              }
          }  
        }
    }
    return 0;
}

例題5、四平方和

【問題描述】

四平方和定理,又稱為拉格朗日定理:每個正整數都可以表示為至多4個正整數的平方和。

如果把0包括進去,就正好可以表示為4個數的平方和。

比如:5 = 0^2 + 0^2 + 1^2 + 2^2 7 = 1^2 + 1^2 + 1^2 + 2^2 (^符號表示乘方的意思)

對於一個給定的正整數,可能存在多種平方和的表示法。

要求你對4個數排序:0 <= a <= b <= c <= d

並對所有的可能表示法按 a,b,c,d 為聯合主鍵升序排列,最後輸出第一個表示法

程式輸入為一個正整數N (N<5000000)

要求輸出4個非負整數,按從小到大排序,中間用空格分開

【輸入形式】例如,輸入:

5

則程式應該輸出:

0 0 1 2

思路:

直接暴力列舉,但如果直接四重迴圈0~n很可能會超時,因此必須進行優化:

1)四平方和 = n——必定有:a * a <= n、b * b <= n、c * c <= n、d * d <= n

2)要求你對4個數排序:0 <= a <= b <= c <= d——迴圈條件:b = a、c=b開始且最後d要>=c

3)減少迴圈至三重,d = sqrt(n - a * a - b * b - c * c)

【參考程式碼】

#include<iostream>
#include<cmath>

using namespace std;

int main()
{
	int n;
	cin>> n;
	for(int a = 0; a * a <= n; a++){
		for(int b = a; b * b <= n; b++){
			for(int c = b; c * c <= n; c++){
				int d = sqrt(n - a*a - b*b - c*c);
				if((d >= c) && (a*a + b*b + c*c + d*d == n))
				{
					cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl; 
				}
			}
		}
	}
	
	return 0;
}

例題6、第100002個素數

【問題描述】

素數就是不能再進行等分的整數。比如:7,11。而9不是素數,因為它可以平分為3等份。一般認為最小的素數是2,接著是3,5,... 請問,第100002(十萬零二)個素數是多少? 請注意:2 是第一素數,3 是第二個素數,依此類推。

思路:

方法一for暴力列舉,將所得素數存到一個陣列中,從下標1開始,輸出q[100002]即可

值得注意的是儲存的資料可能會很龐大,因此陣列要儘量開得很大,而且作為全域性變數!

i的範圍我找了蠻久的(10000000、10000000/2都能得出答案),而且陣列的範圍也要很大才行!

#include<iostream>
#include<cmath>
using namespace std;
int q[1000000]={0};
bool check(int n)
{	
    for(int i = 2; i <= sqrt(n); i++)
    {
        if(n % i == 0) return false;
    }
    
    return true;
}
int main()
{   
    int j = 1;
    for(int i = 2 ; i<10000000/2; i++)
    {
        if(check(i))
        {
            q[j++] = i;
            
        }
    }
    
    cout<<q[100002];
    
    return 0;
}

方法2:為了不那麼麻煩的找範圍,開大範圍陣列,直接用while迴圈找答案,找到即結束!

#include<iostream>
#include<cmath>
using namespace std;

bool check(int n)
{
    for(int i = 2; i<= sqrt(n); i++)
    {
        if(n % i == 0) return false;
    }
    
    return true;
}
int main()
{   
	int count = 0;
	long long x = 2;
	while(count != 100002 )
	{
		if(check(x)) count ++;
		x++;
	}
    cout<<x - 1;
    return 0;
}

注:如果文章有任何錯誤或不足,請各位大佬盡情指出,評論留言留下您寶貴的建議!如果這篇文章對你有些許幫助,希望可愛親切的您點個贊推薦一手,非常感謝啦

image