peienwu
    • Create new note
    • Create a note from template
      • 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
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
      • No invitee
    • Publish Note

      Publish Note

      Everyone on the web can find and read all notes of this public team.
      Once published, notes can be searched and viewed by anyone online.
      See published notes
      Please check the box to agree to the Community Guidelines.
    • 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
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync 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
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
No invitee
Publish Note

Publish Note

Everyone on the web can find and read all notes of this public team.
Once published, notes can be searched and viewed by anyone online.
See published notes
Please check the box to agree to the Community Guidelines.
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
# 資訊之芽第八週—動態規劃(二) ###### tags: `資訊之芽` ###### tags: `2021暑期筆記` 這兩週真是風風雨雨,首先第一週是段考週,所以有很多時間拿去惡補段考。第二週則是疫情關係要在家裡線上上課 :cry: 結果進度就辣了一大截...趕快追! ## 上課內容 一堆的背包問題(真的很多種耶) **01背包問題** **無限背包問題** - 先固定背包數量看價值跟先固定價值看背包數量可以互換 **有限背包問題** - 多枚舉每個物品要放入的數量 - 拆分:把同重量的物品看成不同的物品(複雜度不變) - 把ti 個物品拆成floor(log_2(t_i+1)+個物品 - 分成的堆數最少:ceil( log(t_i+1)) (因為分成k堆最多只可能有2^k個值) - 複雜度:O(n*log(max{ti})*W) **混合背包問題** - 遇到什麼樣的物品就怎麼做(對有限個背包拆分) - 分成01背包跟無限背包做 **二維背包問題** - 多一維度狀態轉移(可以壓掉一個維度) **分組背包問題** - 再多一維儲存第幾組 **背包合併** - 直接把物品混起來做 **背包問題變化** - (1)求最大價值的方法總數 - 用g[i] 儲存重量i 的方法數 - (2)求最大價值的一組方案 - g[i] 看有沒有被更新過,有就g[I] = 1,回溯找 - (3)求最大價值的字典序最小的一組方案 - 把物品倒過來(由大到小)因為越小的(1)要最後考慮,否則1先考慮後面的大的數字會把1覆蓋掉,字典序就變大 - (4)求次大價值的解/第K大價值的解 - 看投影片 **分數背包** - Greedy **不同做法複雜度** - 用價值做狀態 - 用重量做狀態 - <font color="#f00">V, W都很大但n很小? - 枚舉$2^n$ <font color="#000"> - 如果V, W都很大n也蠻大? - 折半枚舉(meet in the middle)根號算法 ## 上機作業 背包問題主要有三個變量:價值、重量、物品數量,因此可以有三個作法: 1. 以物品數量n 作為狀態,爆搜,複雜度:$O(2^n)$ 2. 以價值v 作為狀態(v為物品價值總和),dp作,複雜度:$O(NV)$ 3. 以重量w 作為狀態(w為物品重量上限),dp作,複雜度:$O(NW)$ ### 高棕櫚農場 [題目連結](https://neoj.sprout.tw/problem/157/) 這一題不能用重量做,因為重量的範圍可以到$10^5$,因此只能用價值來做 有一個要點,無限大可以memset定義為**0x3f3f3f3f**,以十進位表示1061109567,在int的範圍但不會超過 #### 定義 定義$f(n,m)$為取n樣物品,價值恰為m,重量總和最小值 #### 轉移方式 $f(n,m) = min(f(n-1,m), f(n-1,m-v_n)+w_n), m ≧ v_n$ $f(n,m) = f(n-1,m), m < v_n$ #### 邊界條件 f(0,0) = 0, f(0,k) = INF (k>0)$ 因為取零樣物品價值要k不可能達到,因此重量設為無限大 我們可以藉由滾動dp來節省空間,壓成一維(跟用重量作為狀態一樣) 最後,在從dp裡面取出max(k), for all f(N,k) ≦ W #### 程式碼 ```cpp= #include <bits/stdc++.h> #define int long long #define INF 0x3f3f3f3f #define ios ios::sync_with_stdio(0),cin.tie(0) using namespace std; int t; signed main(){ ios; cin>>t; while(t--){ int n,m,val[105],weight[100005];cin>>n>>m; for(int i=1;i<=n;i++)cin>>weight[i]>>val[i]; int dp[100005]; //定義f(n,m)取n樣物品,價值為m,重量總和最小 //dp[i]:價值為i時,重量最小為dp[i] memset(dp,INF,sizeof(dp)); dp[0] = 0; for(int i=1;i<=n;i++){ for(int j=10000;j>=val[i];j--){ dp[j] = min(dp[j],dp[j-val[i]]+weight[i]); } } int ans = 0; for(int i=0;i<=10000;i++) if(dp[i] <= m && i > ans)ans = i; cout<<ans<<endl; } } ``` ### 高棕櫚農場2 [題目連結](https://neoj.sprout.tw/problem/158/) 有些細節是必須要注意的,也就是初始化的細節。 :::success **背包問題是否恰好裝滿** 對於原本初始化dp[0] = 0,代表對於重量限制為0的背包價值最高為0 接下來有兩種情況需要討論,第一種是重量限制為w的背包最多的價值 **1. 恰好裝滿** 此時必須初始化dp[i] = -INF,是因為要恰好裝滿的關係,初始化的dp 數組事實上就是**在沒有任何物品可以放入背包時的合法狀態**,其他除了0之外容量的背包均沒有合法的解,屬於未定義的狀態,所以都應該被賦值為 −∞ 。當前的合法解,一定是從之前的合法狀態推得的(−∞跟−∞取max還是−∞) **2. 不需恰好裝滿** 如果背包並非必須被裝滿,那麼任何容量的背包都有一個合法解“什麼也不裝”,這個解的價值為0,所以初始化時狀態的值也就全部為0了。 如果來看轉移式,$dp[j] = max(dp[j],dp[j-weight[i]]+val[i])$,如果兩者的狀態都屬於未定義,對於需恰好裝滿的狀況,兩者都是−∞,表示沒有合法的狀態可以構成此重量。同時,如果不需恰好裝滿的情況,即使$dp[j]$和$dp[j-weight[i]]$都未定義(等於0),還是可以被更新(在沒有裝滿的情況下,dp[j] = val[i]) ::: 這一題除了以上發現,還有一個很重要的東西,就是迴圈到底要放哪一層的問題。主要是卡在 for(int p=1;p<=k;p++)到底要放在哪一層的問題,結果是要放在第三層。 :::info 問題一:dp[j][p]取決於dp[j][p] 和dp[j-weight[i]][p-1],而且對於一個物品最多只能放一次,如果放在第二層,dp[j-weight[i]][p-1] 就已經被更新過了,有可能已經取了第 i 樣物品會有重複取的問題,如果放在第三層,代表對每一種不同的重量先更新放入幾樣物品的1到k,再更新重量,這樣就可以保證dp[j][p]不會取到已經更新的格子(dp[j][p] 沒被更新、dp[j-weight[i]][p-1] 其中第一維的j-weight[i] 也還沒被更新) 問題二:p要從1到k還是k到1,這其實都可以,因為要取的格子不管從前往後或後往前取都只會取到上一輪(i-1) 的更新東西,因此不影響。還有,因為是定義**最多取p樣物品**,所以無論i為多少,每一次p皆要更新的k(如果k=5,取一樣物品也符合情況) ::: #### 定義 定義$f(j,p)$看完 *i* 樣物品後,重量限制為j,**最多**取p樣物品的最大價值 #### 轉移方式 $dp[j][p] = max(dp[j][p],dp[j-weight[i]][p-1]+val[i])$ #### 邊界條件 $dp[i][j] = 0$ (for all elements in dp) ```cpp= #include <bits/stdc++.h> #define int long long #define INF 0x3f3f3f3f #define ios ios::sync_with_stdio(0),cin.tie(0) using namespace std; int t; signed main(){ ios; cin>>t; while(t--){ int n,m,k,val[105],weight[10005];cin>>n>>m>>k; for(int i=1;i<=n;i++)cin>>weight[i]>>val[i]; int dp[1005][105]; memset(dp, 0, sizeof(dp)); for(int i=1;i<=n;i++){ for(int j=m;j>=weight[i];j--){ for(int p=1;p<=k;p++){ dp[j][p] = max(dp[j][p],dp[j-weight[i]][p-1]+val[i]); } } } cout<<dp[m][k]<<endl; } } /* 0 4 5 6 6 6 0 4 5 6 9 10 0 4 5 6 9 10 */ ``` ### 玩電梯 [題目連結](https://neoj.sprout.tw/problem/416/) [題目連結2](https://codeforces.com/problemset/problem/479/E) 這一題要用到3個重要的技巧:前綴和、差分、滾動dp 差分在某一次手寫作業有寫到,不過那時候沒有很注意這個部分就是了:smile: :::info **差分** 差分是前綴和的逆運算,也就是說,把兩項的差算出來就是差分。定義如下: $$b_i = \begin{cases}a_i-a_{i-1}, &\text{if }i\gt 1 \\a_1, & \text{if } i = 1\end{cases}$$ 差分的使用時機是區間加值,一個區間內的數字都加上一個定值,這時候就可以使用到差分的技巧。使用方式如下,當我要在區間 $[l,r]$ 的每一個數字都加上一個值$v$,以下步驟: 1. 定義一個新的陣列 $b_i$ 表示每一項差分 2. 設 $b_l = b_l+v$,$b_{r+1} = b_{r+1}-v$ 3. 將差分的每一項加上前一項,即為原數列 $b_i = b_{i-1}+b_i$ 第二步驟可以重複好幾次做,這樣複雜度從原本的$O(n)$就變成了O(1)了! ::: 這一題使用到差分的技巧,讓原本的$O(kn^2)$減少成$O(kn)$,然後就可以過了! #### 定義 定義 $dp[i][j]$ 為第 i 次走到樓層j的方法數 #### 轉移式 這題如果用拉的比較不好想,所以改用推的試試看 $dp[i+1][j] = dp[i+1][j]+dp[i][p],$ for $j\in[p-r],[p+1,p+r],r = |p-b|-1$ #### 邊界條件 $dp[0][a] = 1$ 轉移式比較複雜一點,不過可以用差分優化搭配前綴和把原本$O(n)$的時間降到$O(1)$ 從這一題可以發現到,用拉的和用推的有不同的使用時機,可以以思考方式比較清楚的想法去想轉移式。 ```cpp= #include <bits/stdc++.h> #define int long long #define mod 1000000007 #define ios ios::sync_with_stdio(0) using namespace std; int n,a,b,k,dp[2][2005]; int modify(int x){ return (x%mod+mod)%mod; } void sec(int l,int r,int v,int id){ l = max(l,(long long)1); r = min(r,n); dp[(id+1)%2][l] = modify(dp[(id+1)%2][l]+v); dp[(id+1)%2][r+1] = modify(dp[(id+1)%2][r+1]-v); } signed main(){ ios; cin>>n>>a>>b>>k; memset(dp,0,sizeof(dp)); dp[0][a] = 1; for(int s=0;s<k;s++){ //每一次電梯移動 for(int i=1;i<=n;i++){ //每一樓層轉移 int d = abs(b-i)-1; sec(i-d,i-1,dp[s%2][i],s); sec(i+1,i+d,dp[s%2][i],s); } for(int i=1;i<=n;i++){ dp[(s+1)%2][i] += dp[(s+1)%2][i-1]; dp[(s+1)%2][i] = modify(dp[(s+1)%2][i]); } for(int i=1;i<=n;i++)dp[(s)%2][i] = 0; } int ans = 0; for(int i=1;i<=n;i++){ ans+=dp[k%2][i]; ans = modify(ans); } cout<<ans<<endl; } //8樓、2樓開始、5樓不能去、2次電梯 ``` ### 取名字好困難QQ [題目連結](https://neoj.sprout.tw/problem/421/) 跟題目一樣,我覺得要通靈才能想到這一題的作法! 結果是問了別人才大概感受到這一種作法!!! ![](https://i.imgur.com/EmxSHqM.png) 我們既然不知道到底一個數字要不要乘2,我們可以透過做LIS的過程來做決定。當我們把乘與2之後的數字跟原本數字一起push進去,就可以發現到LIS不可能同時取到2個數字。利用這個方法就可以用LIS的過程決定一個數字到底應該要變2倍還是不用。 要找到最長的非嚴格遞增序列,最大的差別就是要把原本的lower_bound改成upper_bound。一整天想一題的感覺超級糟糕:cry: ```cpp= #include <bits/stdc++.h> #define int long long #define mod 1000000007 #define FOR(i,n) for(int i=0;i<n;i++) #define ios ios::sync_with_stdio(0) using namespace std; int t; void solve(){ int n,m;cin>>n>>m; vector<int>vec; FOR(i,n){ int temp;cin>>temp; if(2*temp<m)continue; else if(temp<m){ temp*=2; vec.push_back(temp); } else{ vec.push_back(2*temp); vec.push_back(temp); } } if(vec.size()<1){ cout<<0<<endl; return; } //正常做LIS vector<int> lis;int len = vec.size(); lis.push_back(vec[0]); for(int i=1;i<len;i++){ if(lis.back()<=vec[i])lis.push_back(vec[i]); else *upper_bound(lis.begin(),lis.end(),vec[i]) = vec[i]; } cout<<lis.size()<<endl; } signed main(){ ios; cin>>t; while(t--){ solve(); } } ``` ## 手寫作業 這一週是講歸約法,作業如下: ![](https://i.imgur.com/amWVYo0.jpg) 這一週手寫作業的狀況還不錯,71/75,不過第一題是緊急向別人求救才把答案改掉 ![](https://i.imgur.com/GMtrB99.png) 原本是寫B,後來改A,原因有以下兩點: 1. 因為函數在 return 時不會把 stack memory 上的資料清空,所以所有的區域變數都會被留在 stack memory 上,其他函數可能會共用到同一塊記憶體空間。 2. 有可能會對非法的陣列位址取值(RE的情況),而存取到別的函數的區域變數。 所以A是錯誤的! 下面是解答的畫法,其實跟我蠻像的XDD ![](https://i.imgur.com/NyN0Kwy.png)

Import from clipboard

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 is not available.
Upgrade
All
  • All
  • Team
No template found.

Create custom 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

How to use Slide mode

API Docs

Edit in VSCode

Install browser extension

Get in Touch

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
Upgrade to Prime Plan

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

No updates to save
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

      Upgrade

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Upgrade

      Danger Zone

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

      Syncing

      Push failed

      Push successfully