臺中一中39th電研社教學
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- title: DSA 第十週講義 臺中一中電研社 slideOptions: theme: sky transition: 'convex' --- <style> h2{ color:#8B4746; } .blue{ color:#4A919E } </style> <font color="#4A919E">DSA 第十週講義</font> === >[name= 沈奕呈、陳睿倬][time=Apr 1,2022 ] ###### tags:`DSA` `Data Structure and Algorithm` `資料結構` `演算法` `Data Structure` `Algorithm` `tcirc39th` `社課` `臺中一中電研社` [TOC] --- ## <div class="blue">電研社</div> 社網:[tcirc.tw](https://tcirc.tw) online judge:[judge.tcirc.tw](https://judge.tcirc.tw) IG:[TCIRC_39th](https://www.instagram.com/tcirc_39th) 社課點名表單:[google 表單](https://judge.tcirc.tw/ShowProblem?problemid=c053) ~~社課點名表單:[google 表單](https://reurl.cc/g0V1mQ)~~ --- ## Greedy(貪婪法) 每次都採取該問題下的最佳解,而最後的輸出就是每個子問題的最佳解總和。 通常遇到這類題型,我們要做的就是尋找題目答案的原則(規則),再不斷地依據條件計算或更改輸出。 ---- ### 練習: [Add All](https://zerojudge.tw/ShowProblem?problemid=d221) 根據題目要求,我們要相加一個數列。而每兩個數字的相加成本為其相加後的總和。例如: 3+5 = 8 --> cost: 8 我們可以觀察一下範例測資,尋找輸出最小成本的規則 ---- 解答: 因為相加後的數視為一個新的數,所以n個數固定要加n-1次,導致每次相加的兩個數越小越好(目前最小的兩個數相加) ---- #### 細節: 1. 注意輸入大小,或相加過程中是否溢位 2. 時間: 由於此題輸入量5000 --> 如何有效率地尋找最小值,並在相加後排序 ---- #### priority_queue 在寫競程時,排序有助於我們方便地對資料套用一些演算法。但同時也要注意排序所花的時間。 在這裡,每相加一次就要做排序 --> 若今天數列5000個 --> 做5000-2次排序。 此時,可以考慮STL以幫你寫好的容器且會排序,如:set、mutliset還有這裡的priority_queue(以下簡寫pq) ---- 由於pq不是greedy的重點,這裡不會講太多,可以參考下面網址 [連結1](https://www.geeksforgeeks.org/priority-queue-in-cpp-stl/) [連結2](https://www.cplusplus.com/reference/queue/priority_queue/) [連結3](https://yuihuang.com/cpp-stl-priority-queue/) * pq.push(x): 將x加進pq * pq.pop(): 將最大(小)的數字取出 * pq.top(): 檢視最大(小)的數字 * pq.size(): 回傳pq長度 * pq.empty(): 回傳有無數字 ```cpp= 宣告 priority_queue<int> pq1 // 由大排到小 priority_queue<int, vector<int>, greater<int>> pq2 // 由小排到大 ``` ---- 程式碼: ```cpp= #include <bits/stdc++.h> using namespace std; typedef long long ll; int const MaxN = 5000; int a[MaxN], N; int main() { ios::sync_with_stdio(0); // 資料有點多,加速讀取 cin.tie(0); while (1){ ll x, cost = 0; // cost可能溢位 priority_queue<ll, vector<int> , greater<int> > pq; cin >> N; if (N <= 0)break; for (int i=0;i<N;i++){ cin >> x; pq.push(x); } while (pq.size()>1){ // 取最小的兩個數字 ll x1, x2; x1 = pq.top(); pq.pop(); x2 = pq.top(); pq.pop(); x = x1+x2; cost+=x; pq.push(x);// 加完記得塞回去 } cout << cost << endl; } return 0; } ``` ---- --- ## DP dynamic programming(動態規劃),是分治法的延伸 DP的分割方法為,<font color="#ff0000">將原問題遞推成數個同性質的子問題</font>,過程中會將每個不同的子問題答案作記憶化儲存,如此一來,再次遇到相同的子問題時便可以直接套用答案,省去不必要的重複計算 所以 DP = Divide & Conquer + memoization ---- 什麼時候適合用DP? 1. 最佳子結構性質:子問題的解可以推到原本的問題的解。 2. 重疊子問題:常常需要處理相同的子問題 - 不符合第一點的話沒辦法用DP - 符合第二點的時候,記憶化儲存就能有效減少執行次數,以空間換取時間 ---- ### DP VS 分治 - <font color="#ff0000">DP:</font> DP的子問題是由原問題遞推出來的,所以常常需要處理相同的子問題 <!--原問題遞推出數個子問題--> - <font color="#ff0000">分治:</font> 分治的子問題是由大問題切割出來的,雖不大重複,但會有跨區問題 ---- ### DP要點 dp 由四大要素組成 1. 尋找遞迴式(recurrence) 2. 定義子問題(state space) 3. 狀態轉移(state transition) 4. 找到原問題的答案 ---- #### 1. 尋找遞迴式(recurrence) 找出這個問題的規律,像解數學一樣寫出遞迴關係式 ---- ex.費式數列 - 設費式數列第n項為f(n) f(n){ n=1 , f(1)=1 n=2 , f(2)=1 n>2 , f(n)=f(n-2)+f(n-1)} ---- #### 2. 定義子問題(state space) (1)在分治法中,我們需要決定切割問題的方法;在dp中,我們則需要知道會出現的子問題有那些,來決定要計算的範圍 (2)開一個陣列當答案的存放區,依據要計算的範圍決定陣列的長度、答案放的位置 ---- ex.尋找費式數列第$n$項 (1)需要計算的子問題:第一項的值、第二項的值、...、第$n$項的值 (2)開個長度為$n+1$的陣列$f$,決定把第i項的值放在 $f[i]$ ps.你也可以直接將陣列長度固定為$max_n+1$,反正題目輸入的資料量不會超過他規定的最大值$max_n$ ---- #### 3. 狀態轉移(state transition) 根據前面求出的遞迴關係式,設定基本的子問題答案,再根據實作方法設計「轉移式」的表示法 如果沒有設定基本的子問題答案,你根本沒有推移子問題的起點(或終點) ---- 將轉移式對應遞迴關係式,程式的實作方法不同,轉移式寫法就不同 ```cpp= //初始子問題答案設定(initialize) f[1]=1; f[2]=1; //轉移式(之後要根據實作方法調整轉移式的表示法) f[n]=f[n-1]+f[n-2]; ``` ---- #### 4. 找到原問題的答案 根據你定義子問題及狀態轉移的方法,推理答案會出現在陣列的哪一格 ---- 第$i$項會放在第$i$格,所以答案在陣列第$n$格 ```cpp= cout<<f[n]; ``` ---- 釐清以上四點後就可以開始實作程式碼了,實作方式分兩種 - top down 使用遞迴由「原問題」推回「基本狀態」 - bottom up 使用迴圈由「基本狀態」推至「原問題」 ---- 用top down的方式比較好思考,但用bottom up的執行時間比較短,所以建議大家先用top down寫寫看,再想要怎麼轉成bottom up --- ### [frog 1](https://atcoder.jp/contests/dp/tasks/dp_a) 1. 設f(n)為跳到第n格的最小花費 f(1)=0 f(n)=min(f(n-2)+h[n-2]與h[n]的高度差 , f(n-1)+h[n-1]與h[n]的高度差) 2. 需計算跳到第1~n塊石頭的花費 `int c[n+1];` 第n格放f(n)的值 4.答案為c[n] ---- ### top down top down 是先問原問題的答案,為了尋求原問題的答案,使用遞迴回推其子問題的答案,當所有衍生出的子問題都推至「基本狀態」後,便能得解 ---- 3. 轉移式設計為 `return c[n]=遞迴式 ;` ```cpp= c[1]=0; int t1=topdown(n-1)+abs(h[n]-h[n-1]);//f(n-1)+樓差 int t2=topdown(n-2)+abs(h[n]-h[n-2]);//f(n-2)+樓差 return c[n]=min(t1,t2); ``` ---- ```cpp= #include <bits/stdc++.h> using namespace std; int n; const int max_n=1e5+1; const int max_v=1e4; const int inf=1<<30;//不會落在答案範圍內 int h[max_n]; int c[max_n]; int topdown(int n){ //終止條件:起點<=第0塊石頭,不可能發生 //所以去掉這個選項->給出一個比所有可能答案都高的值 if(n<1) return inf; if(c[n]==inf){//3.轉移式 int t1=topdown(n-1)+abs(h[n]-h[n-1]);//f(n-1)+樓差 int t2=topdown(n-2)+abs(h[n]-h[n-2]);//f(n-2)+樓差 return c[n]=min(t1,t2);//記得return的時候要記憶化 } return c[n];//算過的子問題的值不會是inf,直接套答案 } int main() { for(int i=1;i<=n;i+=1){ cin>>h[i]; c[i]=inf; } c[1]=0;//基本的子問題答案 cout<<topdown(n)<<'\n'; } return 0; } ``` ---- ### bottom up bottom up 藉由基本的子問題答案,使用for迴圈一步步組合成母問題的答案,當組合出原問題的答案,便能得解 ---- 3. 轉移式設計為 `c[n]=遞迴式 ;` ```cpp= c[1]=0; int t1=c[i-1]+abs(h[i]-h[i-1]); int t2=c[i-2]+abs(h[i]-h[i-2]); c[i]=min(t1,t2); ``` ---- ```cpp= #include <bits/stdc++.h> using namespace std; int n; const int max_n=1e5+1; const int max_v=1e4; const int inf=1<<30;//不會落在答案範圍內 int h[max_n]; int c[max_n]; int main() { for(int i=1;i<=n;i+=1){ cin>>h[i]; } c[1]=0; c[2]=abs(h[2]-h[1]); for(int i=3;i<=n;i+=1){ int t1=c[i-1]+abs(h[i]-h[i-1]); int t2=c[i-2]+abs(h[i]-h[i-2]); c[i]=min(t1,t2); } cout<<c[n]<<'\n'; return 0; } ``` --- ### 背包問題 是DP裡的經典問題。大致上的敘述: 今天有一堆物品,每個物品有各自的價值和重量。所求: 在有重量限制的狀況下選擇物品的價值總和越高越好。 背包問題實際上有兩種,一個是物品可以分割,另一種是不行,就是說只能選或不選。前者可以特過前面講的greedy寫,計算價值/重量的比例,優先選擇比值較高的。 而DP在處理的是後者,又稱 0 1背包問題,是道非常經典的題目。下面我們拿個例題來做示範。 --- #### 例題: [搬家規劃問題](https://zerojudge.tw/ShowProblem?problemid=c147) 我們就來把DP的四大重點套進這個題目吧。先想子問題,由於每個重量有不同的組合方式,所以無法從重量拆問題。所以試試看能不能由物品下手。我們可以從一種物品開始擺。擺完後,再看另外一種物品,若物品有更好的選擇或組合,那就選最大的。 ---- 看起來這方式行得通。但要如何尋找每個狀態下的最佳解呢? 這時,我們可以連同其他重量的選擇也找好。 雖然我們無法從上個重量的答案推知下個重量的答案。 但是,藉由記錄每個重量,當前w的最佳解可以變成物品價值加上$W_{重量差值}$的最佳解,省去直接窮舉的時間。 ---- weight: 1 2 value: 1 3 W: 5 || 0 | 1 | 2 | 3|4|5| |-| - | - | - | - |-|-| |1| 0 |1 |1|1|1|1| |2|0|1|3|4(3+W[1])|4|4| ---- 遞迴式: 這裡區分一下: 物品價值$v_i$, $w_i$ 每個重量最佳解$a_w$ $a_w$ = $max(a_w, v_i+a_{w-w_{i}})$ 狀態轉移: 每當有新物品進來時,掃過陣列一遍,對每個重量最佳解進行更新。但還需要另一個陣列紀錄原陣列答案。 ---- ```cpp= #include <bits/stdc++.h> using namespace std; int const MaxW = 1e6; vector<pair<int, int>> vec; int N, W; int arr[2][MaxW+1]; int ans = 0; bool cmp(pair<int, int> a, pair<int, int> b){ if (a.first != b.first) return (a.first < b.first); else return (a.second < b.second); } int main(){ stringstream ss1, ss2; string s1,s2; getline(cin, s1); ss1 << s1; string x; int idx = 0, a; int nowi = 0, nxti = 1; while (ss1 >> x){ a = stoi(x); vec.push_back({a, 0}); } getline(cin, s2); ss2 << s2; while (ss2 >> x){ a = stoi(x); vec[idx++].second = a; } cin >> W; N = vec.size(); sort(vec.begin(), vec.end(), cmp); //for (int i=0;i<N;i++){ // cout << vec[i].first << ' ' << vec[i].second << endl; //} memset(arr, 0, sizeof(arr)); for (int n=0;n<N;n++){ for (int w = 1;w<=W;w++){ if (vec[n].first > w)continue; arr[nxti][w] = max(arr[nowi][w], arr[nowi][w - vec[n].first]+vec[n].second); } swap(nowi, nxti); } cout << arr[nowi][W] << endl; return 0; } ``` --- ### 練習題 DP的題目到處都是... [AtCoder DP Contest](https://atcoder.jp/contests/dp):Atcoder上的一系列DP題,從簡單到困難都有,解完應該靈力會提升50%(? [AP325](https://judge.tcirc.tw/Problems?page=4&&tabid=AP325):第六章整章都是DP,開心解ㄅ\~大部分都不會太難,~~裡面似乎混了幾題有點可怕的東東~~ (解得一點都不開心:sob: by教學長)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully