日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【算法】蛮力法/穷举法/枚举法 的基本问题分析

發(fā)布時間:2023/11/30 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【算法】蛮力法/穷举法/枚举法 的基本问题分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
  • 炮兵問題的優(yōu)化,設(shè)立邏輯數(shù)組

蠻力法設(shè)計思想

有策略地窮舉 + 驗證

  • 制定窮舉策略
  • 避免重復(fù)

簡單來說,就是列舉問題所有可能的解,然后去看看是否滿足題目要求,是一種逆向解題方式。(我也不知道答案是什么,但是我知道答案肯定在這些范圍里面,我一個個試就完了。)

所以我們應(yīng)該做的是

  • 知道可能的答案都有哪些
  • 使用方法將其一一列舉
  • 將每個解依次與條件比照

蠻力法使用范圍

  • 所有解空間:例如a,b,c取值范圍都是 0~9,問abc組成的三位數(shù)的所有情況
  • 所有路徑:遍歷圖、遍歷樹

蠻力法的格式

蠻力法由循環(huán)和選擇語句構(gòu)成

  • 使用循環(huán),窮舉所有情況
  • 使用選擇,判斷當(dāng)前情況是否成立
    • 若成立,則得到問題的解
    • 若不成立,則繼續(xù)循環(huán)

循環(huán) + 分支

for(){for(){// 獲取X所有可能的情況if(X滿足條件){printf("X");}} }
蠻力法思想:窮舉所有情況,找到符合條件的。

這也意味著,得能夠窮舉,因此問題規(guī)模不能太大

示例1:求完全數(shù)


思想:窮舉所有數(shù)字,將符合條件的找出來。

關(guān)鍵點:求數(shù)字的所有因子。
對于數(shù)x,因子y,需要x % y = 0,此處y也需要嘗試1 ~ x-1的所有數(shù)。

判斷因子和是否等于x。

因此程序為

// 求完全數(shù) #include <iostream> using namespace std;int main() {for (int i = 2; i <= 1000; i++) {int sum = 0;for (int j = 1; j <= i - 1; j++) {if (i % j == 0) { // 若是因子sum += j;}}if (sum == i) {cout << "完全數(shù):" << i << endl;}}return 0; }

這里還有點bug,是數(shù)學(xué)知識不完備,因子在1 ~ m/2的范圍,因子不可能比原數(shù)字的一半還要大(除了它本身),我們循環(huán)地太多了。內(nèi)循環(huán)應(yīng)該是j <= i/2。

結(jié)果為:

我們需要非常清晰地體會到構(gòu)建程序框架的思維過程

int main() {// 窮舉所有情況for (int i = 2; i <= 1000; i++) {// 求因子之和// 比較因子與該數(shù)是否相等}return 0; }

然后,我們再實現(xiàn)其細節(jié)。

充分體會:窮舉情況,再依次判斷的計算過程。

示例2:求水仙花數(shù)

在數(shù)字100~999中,求水仙花數(shù),例如:13 + 53+ 33 = 153。

同樣地,窮舉 + 驗證

關(guān)鍵點:求一個三位數(shù)的每一位的數(shù)。

先建立思維框架

// 窮舉100~999的所有數(shù)字 for(){// 求三位數(shù)的每一位數(shù),將其立方和相加// 判斷數(shù)字與其立方和是否相等}

代碼為:

// 求水仙花數(shù) #include <iostream> using namespace std;int main() {for (int i = 100; i <= 999; i++) {int sum = 0;int temp = i;while (temp){int x = temp % 10;sum += (x*x*x);temp /= 10;}if (i == sum) {cout << "水仙花數(shù):" << i << endl;}}return 0; }

答案為:

再強調(diào)一遍

  • 循環(huán) + 選擇
  • 先構(gòu)建思維框架,再填充細節(jié)

示例3:象棋算式


我們先將問題的輸入找到:也就是5個變量,且滿足

  • 每個變量范圍是0~9
  • 5個變量各不相同

然后窮舉所有的可能性,看是否能夠滿足表達式,這是恐怖的5層循環(huán),但是問題規(guī)模還在可接受范圍內(nèi)。

我們試試看。

  • 兵:a
  • 炮:b
  • 馬:c
  • 卒:d
  • 車:e
#include <iostream> using namespace std;int main() {int a, b, c, d, e;for (a = 0; a <= 9; a++) {for (b = 0; b <= 9; b++) {for (c = 0; c <= 9; c++) {for (d = 0; d <= 9; d++) {for (e = 0; e <= 9; e++) {// 若5個數(shù)都不同,再看是否滿足表達式if (!(a == b || a == c || a == d || a == e|| b == c || b == d || b == e|| c == d || c == e|| d == e)) {int add1 = a * 1000 + b * 100 + c * 10 + d;int add2 = a * 1000 + b * 100 + e * 10 + d;int sum = e * 10000 + d * 1000 + c * 100 + a * 10 + d;if (sum == add1 + add2) {cout << "兵 = " << a << " 炮 = " << b << " 馬 = " << c << " 卒 = " << d << " 車 = " << e << endl;}}}}}}}return 0; }

結(jié)果是

不過顯然,太低效率了,如果有n的話,這個算法的時間復(fù)雜度是O(n5),這幾乎是不可接受的,太暴力了,問題規(guī)模顯得有點大,是105了。

所以,我們雖然遵循了 循環(huán) + 選擇,但是,窮舉地過于直接,看看有沒有其他辦法呢?

思想:即便是窮舉,也要“聰明地”窮舉

同樣是窮舉,也有不同的方法,最粗暴的就是循環(huán),一層循環(huán)不行就嵌套多重循環(huán)……好蠢但是很簡單好想。

我們來看看,同樣是窮舉,不同做法的差異。

示例

對于一個含有6個元素的一維數(shù)組,該數(shù)組每個數(shù)只能是0或1,將所有情況窮舉出來。
例如(0,0,0,0,0,0)、(0,1,0,1,1,0)

最愚蠢的做法,直接6重循環(huán)

#include <iostream> using namespace std;int main() {int a, b, c, d, e, f;int number[6];for (a = 0; a <= 1; a++) {for (b = 0; b <= 1; b++) {for (c = 0; c <= 1; c++) {for (d = 0; d <= 1; d++) {for (e = 0; e <= 1; e++) {for (f = 0; f <= 1; f++) {number[0] = a;number[1] = b;number[2] = c;number[3] = d;number[4] = e;number[5] = f;for (int i = 0; i < 6; i++) {cout << number[i] << " ";}cout << endl;}}}}}}return 0; }

結(jié)果:
這的確可以完成任務(wù),但是這樣的算法真的很糟糕,不是嗎?

我們來看看優(yōu)化后的窮舉的方法

我們將其與二進制結(jié)合,因為對于這樣的0、1序列,與二進制有直接聯(lián)系,我們直接將十進制是0~63,轉(zhuǎn)換為二進制,存入數(shù)組中,就可以了,這樣大大降低了時間復(fù)雜度!

#include <iostream> using namespace std;int main() {int number[6];for (int i = 0; i <= 63; i++) {// 轉(zhuǎn)換為二進制,除二求余再倒置int temp = i;for (int j = 5; j >= 0; j--) {number[j] = temp % 2;temp /= 2;}// 輸出結(jié)果for (int k = 0; k < 6; k++) {cout << number[k] << " ";}cout << endl;}return 0; }

窮舉加驗證,循環(huán)加選擇,蠻力解難題

窮舉有方法,驗證有策略,循環(huán)盡量淺,選擇看題目

我們之前談過,窮舉法,需要能夠窮舉,數(shù)據(jù)規(guī)模不太大,現(xiàn)在,我們在此基礎(chǔ)上進行了優(yōu)化,同樣能夠窮舉的,窮舉方式的選擇也很重要。

對于循環(huán),我們希望盡可能減少嵌套層數(shù)

當(dāng)然了,簡單的屬于通用普適技法,稍難的就需要動用你的觀察力,但是這些東西你熟悉了,也就變成了簡單的了。

接下來我們嘗試優(yōu)化示例3。

示例3優(yōu)化

我們只關(guān)注,如何進行窮舉,并且盡可能減小窮舉規(guī)模空間,關(guān)注一個條件:5個數(shù)各不相同

這樣一來,我們的問題規(guī)模就由105 = 10,000 變成了 10 x 9 x 8 x 7 x 6 = 30,240 ,變成了原來的30%左右。

可以用多重循環(huán)來列舉出它們各種不同的取值情況,逐一地判斷它們是否滿足上述等式;為了避免同一數(shù)字被重復(fù)使用,可設(shè)立邏輯數(shù)組x,x[i](0≤i≤9)值為1時表示數(shù)i沒有被使用,為0時表示數(shù)i已被使用。

int main() {int x[10];int a, b, c, d, e, i, m, n, s;for (i = 0; i <= 9; i++) x[i] = 1; /*x數(shù)組置初值*/for (a = 1; a <= 9; a++){x[a] = 0; /*表示不能再讓其他變量取與a相同的值*/for (b = 0; b <= 9; b++)if (x[b]) /*如果b取的當(dāng)前值未被其他的變量重復(fù)*/{x[b] = 0; /*表示不能再讓其他變量取與b相同的值*/for (c = 0; c <= 9; c++)if (x[c]) /*如果c取的當(dāng)前值未被其他的變量重復(fù)*/{x[c] = 0; /*表示不能再讓其他變量取與c相同的值*/for (d = 0; d <= 9; d++)if (x[d]) /*如果d取的當(dāng)前值未被其他的變量重復(fù)*/{x[d] = 0; /*表示不能再讓其他變量取與d相同的值*/for (e = 0; e <= 9; e++)if (x[e]){m = a * 1000 + b * 100 + c * 10 + d;n = a * 1000 + b * 100 + e * 10 + d;s = e * 10000 + d * 1000 + c * 100 + a * 10 + d;if (m + n == s)printf("兵:%d 炮:%d 馬:%d 卒:%d車:%d\n",a, b, c, d, e);}x[d] = 1; /*本次循環(huán)未找到解,讓d取其他值*/}x[c] = 1; /*本次循環(huán)未找到解,讓c取其他值*/}x[b] = 1; /*本次循環(huán)未找到解,讓b取其他值*/}x[a] = 1; /*本次循環(huán)未找到解,讓a取其他值*/}return 0; }

重要的收獲:窮舉有方法,不能上來題都不看就開始舉,有條件地窮舉,減少解空間,對于本題,5個數(shù)不同,可以當(dāng)作題目條件來驗證,也可以當(dāng)成窮舉的約束條件

同樣的條件,不同的看法,就會產(chǎn)生不同的解決方案。

還記得離散數(shù)學(xué)中的附加條件證明法嗎,將結(jié)論中的前提當(dāng)條件用,再推出最終結(jié)論,極大簡化了運算。

百元買百雞問題

已知公雞5元一只,母雞3元一只,小雞1元三只,用100元買100只雞,問公雞、母雞、小雞各多少只?

  • 知道解空間
    • 公雞:x 0~20
    • 母雞:y 0~33
    • 小雞:z 0~100
  • 如何列舉
    • 最簡單思路:三重循環(huán)
  • 如何驗證
  • x + y + z = 100
  • 5x + 3y + z/3 = 100
  • z % 3 == 0
  • 這樣看來,可以將z變量去掉了,解空間也減少了100倍,三重循環(huán)也變成了二重循環(huán)。也就是我們將驗證條件當(dāng)成解空間的約束條件,減少了解空間的規(guī)模,這與我們上面的示例3優(yōu)化是一個思路。
  • 注意:小雞是1元3只,需要注意 z/3 時,int會去掉小數(shù)點,需要驗證z是3的倍數(shù)。

    #include <iostream> using namespace std;int main() {int x, y, z;for (x = 0; x <= 20; x++) {for (y = 0; y <= 33; y++) {if ((100 - x - y) % 3 == 0) {if ((5 * x + 3 * y + (100 - x - y) / 3) == 100) {cout << "公雞:" << x << endl;cout << "母雞:" << y << endl;cout << "小雞:" << 100 - x - y << endl;cout << "**********" << endl;}}}}return 0; }

    示例

    求所有的三位數(shù),它除以11所得的余數(shù)等于它的三個數(shù)字的平方和.

    分析:

  • 解空間:三位數(shù)x,100~999
  • 如何枚舉:循環(huán)
  • 如何驗證(約束條件):x % 11 == 三個數(shù)字平方和
  • 約束條件可否去限制解空間? 否
  • #include <iostream> using namespace std; // 求所有的三位數(shù),它除以11所得的余數(shù)等于它的三個數(shù)字的平方和. int main() {for (int i = 100; i <= 999; i++) {// 求三個數(shù)平方和int sum = 0;int temp = i;while (temp){int x = temp % 10;sum += (x * x);temp /= 10;}// 驗證if (sum == i % 11) {cout << "數(shù)字:" << i << endl;}}return 0; }

    結(jié)果:

    對問題進行建模

    我們進一步分析上一題

    求所有的三位數(shù),它除以11所得的余數(shù)等于它的三個數(shù)字的平方和.

    假設(shè)3位數(shù)A的三個數(shù)是x,y,z。

  • 0 <= A % 11 <= 10
  • x2 + y2 + z2 <= 10,x*100 + y*10 + z = A
  • 1 <= x <= 3, 0 <= y <= 3,0 <= z <= 3,且都是整數(shù)
  • 這樣一來,經(jīng)過簡單的人工處理,解空間減少了很多,然后再進行 窮舉 + 驗證 就可以了,顯然比之前的更加高效,因為進行了人工分析

    int x, y, z; for (x = 1; x <= 3; x++) {for (y = 0; y <= 3; y++) {for (z = 0; z <= 3; z++) {int num = x * 100 + y * 10 + z;if ((x*x + y * y + z * z) == num % 11) {cout << "數(shù)字:" << num << endl;}}} }

    這里強調(diào)的其實也是,將驗證條件進一步分析,將其轉(zhuǎn)換為解空間的約束條件,以降低解空間規(guī)模。

    窮舉 + 驗證:窮舉范圍、窮舉方式、驗證條件、約束條件

    我們再回顧之前的例子,充分體會窮舉法的分析思路

    窮舉依賴的技術(shù)是遍歷,也就是解空間的每個解,都要找一遍,注意盡量避免充分。

    示例1

    • 解空間:2~1000
    • 驗證條件:各因子之和等于本身
    • 約束條件:沒有可以轉(zhuǎn)化的驗證條件
    • 窮舉方式:解空間 + 約束條件–>循環(huán)

    示例2

    在數(shù)字100~999中,求水仙花數(shù),例如:13 + 53+ 33 = 153。

    • 解空間:100~999
    • 驗證條件:水仙花數(shù)
    • 約束條件:無
    • 窮舉方式:解空間+約束條件–>循環(huán)

    示例3

    • 解空間:5個變量,每個變量范圍0~9
    • 驗證條件:滿足題目表達式
    • 約束條件:5個變量各不相同
    • 窮舉方式:解空間+約束方式–>減小解空間–>5重循環(huán),如果有值重復(fù),則跳出

    示例4

    對于一個含有6個元素的一維數(shù)組,該數(shù)組每個數(shù)只能是0或1,將所有情況窮舉出來。
    例如(0,0,0,0,0,0)、(0,1,0,1,1,0)

    注意:本題本身就是求解空間

    • 解空間:6個數(shù),每個數(shù)是0或1,求全部的0、1序列
    • 驗證條件:情況不重復(fù)
    • 約束條件:無
    • 窮舉方式:解空間+約束條件–>6重循環(huán)

    問題建模 & 轉(zhuǎn)化:類二進制數(shù)

    很明顯這題的0、1序列,與二進制數(shù)類似,可以轉(zhuǎn)換問題,改為求0~63十進制的二進制,間接解題。

    屬于特殊解法,需要驚人的洞察力。

    示例5

    已知公雞5元一只,母雞3元一只,小雞1元三只,用100元買100只雞,問公雞、母雞、小雞各多少只?

    • 原始解空間:3個變量,x∈[0,20],y∈[0,33],z∈[0,100]
    • 驗證條件
      • x + y + z = 100(看做約束條件)
      • 5x + 3y + z/3 = 100
      • z / 3為整數(shù)(z % 3 = 0)
    • 約束條件:將驗證條件中第一條,轉(zhuǎn)換為約束條件,也就是z = 100 - x -y這與就去掉了一個變量,這個變量范圍最大,能夠充分減少解空間。
    • 約束后解空間:2個變量,x∈[0,20],y∈[0,33]
    • 窮舉方式:雙重循環(huán)

    注意:誰是驗證條件,誰是約束條件,是跟你的看法有關(guān)的。

    示例6

    求所有的三位數(shù),它除以11所得的余數(shù)等于它的三個數(shù)字的平方和.

    • 原始解空間:數(shù)字A,三個位是x,y,z,A范圍100~999
    • 驗證條件
      • A % 11 = x2 + y2 + z2
    • 約束條件:由驗證條件進一步分析得出,需要有經(jīng)驗和洞察力
      • A % 11 ∈[0,10]
      • x∈[1,3],y∈[0,3],z∈[0,3]
    • 約束后解空間:x∈[1,3],y∈[0,3],z∈[0,3]
    • 窮舉方式:三重循環(huán)

    備注:驗證方式需要具體問題具體分析

    充分體會不同算法的組合,因為一道題目,可能背后涉及到多個不同的算法的組合。

    總結(jié)

    以上是生活随笔為你收集整理的【算法】蛮力法/穷举法/枚举法 的基本问题分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。