# 窮舉和貪婪 ## 4/23 資訊讀書會 --- # 窮舉和貪婪 ---- 窮舉和貪婪都是以最直覺的方法去解決題目,但是也有各自的缺點,需要去判斷適不適合用。 --- ## 暴力搜尋法(窮舉)-概念 ---- * 窮舉就是把所有可能的答案一個個試過,沒有漏掉的情況。 * 缺點是每一個都要試,會花太多時間。 * 不過因為很直覺,窮舉可以用來當作基礎,之後再改成其他的樣子。 * 加入條件、規律都可以減少執行次數。 * 像是深度優先和廣度優先都算是窮舉,不過比任意窮舉執行較少次數。 ---- * 深度優先是先把一條跑到底,然後回到上一個分支的地方,再跑到底,一直到跑完。 * 廣度優先是每次都全部往前一步,直到沒有一個可以繼續走下去,類似水往外流。 --- ## 窮舉-例題 ---- ## [ZeroJudeg-Print it all](https://zerojudge.tw/ShowProblem?problemid=a147) 輸出所有大於0、整數、不可以被7整除、小於n的數 (此題有多個輸入,若n=0則結束) ---- 方法:把從1到n-1的整數列出來,再去掉七的倍數 ### 程式碼 ```cpp= #include <bits/stdc++.h> using namespace std; int main(){ int n; while(cin >> n){ if(!n) break; for(int i=1; i<n; i++) if(i%7) cout << i << ' '; cout << '\n'; } return 0; } ``` ---- ## [ZeroJudge-完全平方和](https://zerojudge.tw/ShowProblem?problemid=a059) 給定一個範圍a到b,找出a與b之間所有完全平方數的和。(測資有多筆輸入) ---- $舉出\sqrt{a}到\sqrt{b}之間的整數,並將其平方加總$ ### 程式碼: ```cpp= #include <bits/stdc++.h> using namespace std; int main(){ int n, a, b, c, d, s; cin >> n; for(int i=0; i<n; i++){ s=0; cin >> a >> b; c = ceil(sqrt(a)); d = floor(sqrt(b))+1; for(int i=c; i<d; i++){ s+=i*i; } cout << "Case " << i+1 << ": " << s << '\n'; } return 0; } ``` --- ## 回溯法 * 回溯法是在DFS到底之前,先提前得知這個方法不行,就可以先回去,減少浪費的時間。 ---- ## 經典例題:[八皇后問題](https://zerojudge.tw/ShowProblem?problemid=a160) * 在一個n乘n的棋盤上,把n個皇后擺上,且彼此不能攻擊。(多筆輸入,到0結束) * 這題要輸出整個棋盤,Q為皇后,x為空格 ---- 可以把行、列、兩個方向的斜線儲存皇后位置,皇后不能擺在已經有皇后的行、列、斜線。 程式碼:未完成 --- ## 貪婪演算法-概念 ---- * 貪婪演算法的想法是,如果每個決定都是當下最好的,那整體來說就會是最好的。 * 可是這不一定是對的,有時候會因為過早做決定而得到錯誤答案,這時候就要用動態規劃了。 --- ## 貪婪演算法-例題 ---- ## [ZeroJudge-Product of digits](https://zerojudge.tw/ShowProblem?problemid=d418) 有l個大於0的整數N,找到最小的整數Q,使Q的每一位數乘起來是N。如果不存在這個Q,則輸出-1。 (測資有多筆輸入) ---- ```cpp= #include <bits/stdc++.h> using namespace std; int main(){ int l, n, q; stack<int> st; cin >> l; while(l--){ cin >> n; if(n==1){ cout << "1\n"; continue; } for(int i=9; i>1; i--){ while(n%i==0){ n/=i; st.push(i); } if(n==1) break; } if(n!=1){ cout << "-1\n"; while(!st.empty()) st.pop(); continue; } while(!st.empty()){ cout << st.top(); st.pop(); } cout << '\n'; } return 0; } ``` ---- ## 最大連續和-貪婪解法 這個題目雖然被歸類在動態規劃中,但其實也可以是貪婪的一種。 ---- ## [ZeroJudge-連續元素和](https://zerojudge.tw/ShowProblem?problemid=d784) 已知l個有n個元素的整數數列,找出每個數列連續元素和的最大值。 ---- 貪婪解法: * 一邊累加一邊記得曾經出現過的最大累加值是多少 * 一旦累加的結果變成負值,就把累加歸零 * 同時也要記得最大的負數,如果發現沒有一個大於0,就輸出最大的那個負數 ---- ### 程式碼: ```cpp= #include <bits/stdc++.h> using namespace std; int main(){ int l, n, m1, s, a, m2; cin >> l; while(l--){ m1=-1; s=0; m2=-10000; cin >> n; while(n--){ cin >> a; if(a<0 && a>m2) m2=a; s+=a; if(s>m1) m1=s; if(s<0) s=0; } if(m1>-1)cout << m1 << '\n'; else cout << m2 << '\n'; } return 0; } ``` --- ## 不能用貪婪的例題 這種題目就是用貪婪法有些會錯的題目。 ---- ## DP問題-背包問題 * 在有限的最大重量下,盡可能把總價值最大的組合放進背包中。 * 這個題目的變種包含可否切割、東西的數量(單個、有限、無限)、放入的代價(時間、大小)。 * 湊硬幣也是背包問題的變種,不過重量就是價值。 ---- ## 貪婪的問題 只有可切割的才可以用貪婪解決,否則如果優先挑選價值最大的,有可能會剩下價值很低的縫隙,而這個縫隙跟其他東西合起來的重量,有可能可以換價值更高的。
{"title":"窮舉和貪婪","description":"窮舉和貪婪都是以最直覺的方法去解決題目,但是卻也有各自的缺點,需要去判斷適不適合用。","contributors":"[{\"id\":\"00ad9127-6491-4b3d-829b-7847a217f8e5\",\"add\":7534,\"del\":3961}]"}
    111 views