動態規劃

Dynamic Programming

定義

大問題->小問題

舉個栗子

栗子本栗想上一個十階的樓梯

他一次可以爬一階或兩階

請問總共有多少種走法?

以邏輯方面來看

十階

九階+1

八階+2

(八階+1)+1

(七階+2)+1

(七階+1)+2

(六階+2)+2

#include<bits/stdc++.h>
using namespace std;
int climb(int n)
{
    if(n==1)
        return 1;
    if(n==2)
        return 2;
    else
        return climb(n-1)+climb(n-2);
}
int main()
{
    int n;
    cin>>n;
    cout<<climb(n);
}

再舉個栗子

栗子爬完樓梯很累所以他要來找走到目的地最短路徑

從左上角開始 他每次只能向右走或向下走

每格數字代表行經該格的路徑長

請問走到右下角累計消耗的最短路徑是多少?

0

7 

8

9

1

2

5

1

1

4

0

10

動腦思考一下

因為只能向右或向下走

所以一個格子的前一步一定是來自左方或上方

故根據邏輯推理得知

到達一個格子累計最短的路徑

是該格所代表路徑長及min(左方或上方)之和

0

7 

8

9

1

2

5

1

1

4

0

10

0

7 

8

9

1

2

5

1

1

4

0

10

0

7 

15

9

1

2

5

1

1

4

0

10

0

7 

15

24

1

2

5

1

1

4

0

10

0

7 

15

24

1

2

5

1

1

4

0

10

0

7 

15

24

1

3

5

1

1

4

0

10

0

7 

15

24

1

3

5

1

1

4

0

10

要找min(左方或上方)

由表可知1比較小 故選1

0

7 

15

24

1

3

6

1

1

4

0

10

0

7 

15

24

1

3

6

1

1

4

0

10

0

7 

15

24

1

3

6

1

1

7

0

10

0

7 

15

24

1

3

6

1

1

7

0

10

0

7 

15

24

1

3

6

7

1

7

0

10

0

7 

15

24

1

3

6

7

1

7

0

10

0

7 

15

24

1

3

6

7

1

7

0

17

0

7 

15

24

1

3

6

7

1

7

0

17

0

7 

15

24

1

3

6

7

8

7

0

17

0

7 

15

24

1

3

6

7

8

7

0

17

0

7 

15

24

1

3

6

7

8

7

8

17

故可得最短路徑為8

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m;
    cin>>n>>m;
    int path[100][100]={0};
    for(int i=0;i<n;i++)
    {
        for(int t=0;t<m;t++)
            cin>>path[i][t];//讀入資料
    }

    for(int i=1;i<n;i++)
        path[i][0]+=path[i-1][0];//累計最左直排

    for(int t=1;t<m;t++)
        path[0][t]+=path[0][t-1];//累計最上橫排

    for(int i=1;i<n;i++)
    {
        for(int t=1;t<m;t++)
            path[i][t]+=min(path[i-1][t],path[i][t-1]);
            //取該格左方和上方最小的那個
    }
    cout<<path[n-1][m-1]<<endl;
}

最後一個栗子

背包問題

現在栗子要載運一些貨品,但他體力有限。每一項貨品k都有一個已知的重量wei[k],以及載運的利潤val[k],希望選出其中的若干項,其重量總和不超過100,使得利潤最大。

 

(翻譯:在重量100內最大利潤是多少?)

動腦思考一下

背包裡可以放入比他小的背包

例如容量為10的背包可以放進容量為4和6的背包

以此類推容量為4的背包也可以放入容量為1和3的背包

然後空間盡量不要浪費 最大化利用栗子的體力:D

背包問題

翻譯:在重量100內最大利潤是多少?

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]

wei=重量

val=價值

全部都先除以10比較好講

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]

cin

wei=3

val=6

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]
6 6 6 6 6 6 6 6

這兩個容量只有1和2

放不下3

k[3]=6

代表容量為3的背包可以裝下價值6

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]
6 6 6 6 6 6 6 6

cin

wei=2

val=5

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]
5 6 6 6 6 6 6 6 6

思考:

如果現在新進來的價值配上一個小背包

利潤會變大嗎?

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]
5 6 6 6 6 6 6 6 6

新進來的重量是2 價值是5

若放進重量4的背包會搭配重量2 那他的總價值=新進來的價值5+重量2背包的價值5=5+5=10

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]
5 6 6 6 6 6 6 6 6

新進來的重量是2 價值是5

若放進重量4的背包會搭配重量2 那他的總價值=新進來的價值5+重量2背包的價值5=5+5=10

你沒有那麼多貨品!!!

價值2已經放進去容量為2的背包一次了

再放進去4一次就是放兩次了

你只有一個貨品!

 

解方:從後面開始放入 避免重複放同一個貨品

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]
6 6 6 6 6 6 6 6

cin

wei=2

val=5

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]
6 6 6 6 6 6 6 6

新進來的重量是2 價值是5

若放進重量10的背包會搭配重量8 那他的總價值=新進來的價值5+重量8背包的價值6 =5+6=11

又11>6 故取11

背包問題

k[1] k[2] k[3] k[4] k[5] k[6] k[7] k[8] k[9] k[10]
6 6 11 11 11 11 11 11

因為背包1和2加上新的價值後不大於6

故不變

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    
    while(cin>>n)
    {
        int wei[110]={0},val[10000]={0};
        int k[110]={0};
        for(int i=0;i<n;i++)
            cin>>wei[i]>>val[i];
            
        for(int i=0;i<n;i++)
        {
            for(int t=100;t>=wei[i];t--)
            {//從後面開始放入
                if(k[t-wei[i]]+val[i]>k[t])
                    k[t]=k[t-wei[i]]+val[i];
                   //判定放入後價值是否更高
            }
        }
        cout<<k[100]<<endl;
    }
}

TRY TRY SEE

NO KAHOOT

Made with Slides.com