1. 程式人生 > >動態規劃入門(一)

動態規劃入門(一)

spa turn color and uil ott c++ erro 大數字

2017-09-01 11:29:43

writer:pprp

看sprout臺灣大學acm教學視頻的第一部分:

裏邊涉及到四道小例題

感覺很好就拿來寫了寫:

題意還有代碼說明都在代碼中:

1、最基礎的骨牌問題:

/*
@param:dp 入門
@writer:pprp
@declare:最經典最簡單的dp
@begin:9:00
@end:10:00
@date:2017/9/1
*/

#include <bits/stdc++.h>

using namespace std;

//未優化的最基礎的
int fun(int n)
{
    if(n == 1) return 1;
    if(n == 2
) return 2; return fun(n-1) + fun(n-2); } //自頂向下的記憶化 //top down 小心遞回過深 int dp[101] = {1,1,2}; int fun2(int n) { if(dp[n] != 0) return dp[n]; return fun2(n-1) + fun(n-2); } //自底向上 //子問題一定要比母問題要先跑到,註意遞回跑法 int dp2[101] = {0}; void build() { dp2[1] = 1; dp2[2] = 2; for(int i = 3 ; i < 101
; i++) dp2[i] = dp2[i-1] + dp2[i-2]; } int fun3(int n) { return dp2[n]; } int main() { build(); int a; while(cin >> a) { cout << fun(a) << endl; cout << fun2(a) << endl; cout << fun3(a) << endl; } return
0; }

2、塗色問題:題意見代碼頭

/*
@param:dp 入門
@writer:pprp
@declare:最經典最簡單的dp,給紅綠藍三種顏色,讓你求出藍綠不相鄰的排列n個元素的情況
@begin:10:10
@end:10:28
@date:2017/9/1
*/

#include <bits/stdc++.h>

using namespace std;

/*
狀態確定:按照最後一位的顏色確定狀態
f(n,0):最後一位是紅色的數量
f(n,1):最後一位是綠色的數量
f(n,2):最後一位是藍色的數量
狀態轉移:
f(n,0) = f(n-1,0) + f(n-1,1) + f(n-1,2)
f(n,1) = f(n-1,0) + f(n-1,1)
f(n,2) = f(n-1,0) + f(n-1,2)
初始條件:
f(1,0) = 1;
f(1,1) = 1;
f(1,2) = 1;
最終答案:
f(n,0) + f(n,1) +f(n,2);
*/

// top down
int dp1[101][3] = {0};

void init()
{
    dp1[1][0] = dp1[1][1] = dp1[1][2] = 1;
}

int fun(int n,int m)
{
    if(dp1[n][m] != 0)
        return dp1[n][m];

    if(m == 0) dp1[n][0] = fun(n-1,0) + fun(n-1,1) + fun(n-1,2);
    if(m == 1) dp1[n][1] = fun(n-1,0) + fun(n-1,1);
    if(m == 2) dp1[n][2] = fun(n-1,0) + fun(n-1,2);

    return dp1[n][m];
}

//bottom up
int dp2[101][3] = {3};
int build()
{
    dp2[1][0] = dp2[1][1] = dp2[1][2] = 1;
    for(int i = 2; i < 101 ; i++)
    {
        dp2[i][0] = dp2[i-1][0] + dp2[i-1][1] + dp2[i-1][2];
        dp2[i][1] = dp2[i-1][0] + dp2[i-1][1];
        dp2[i][2] = dp2[i-1][0] + dp2[i-1][2];
    }
}
int fun2(int n)
{
    return dp2[n][0] + dp2[n][1] + dp2[n][2];
}

//run
int main()
{
    int a;
    cin >> a;
    init();
    build();

    cout << fun(a,0) + fun(a,1) + fun(a,2) << endl;
    cout << fun2(a) << endl;
    return 0;
}

3、骨牌問題2,加了一個L型骨牌

/*
@param:dp 入門
@writer:pprp
@declare:最經典最簡單的dp, 給你2*1 & 3*1 L型骨牌填滿2*n的格子,有幾種拍法
@begin:10:36
@end:11:00
@date:2017/9/1
*/

#include <bits/stdc++.h>

using namespace std;

/*
狀態分析:
f(n) 代表的是放n*2個格子時候的方法數
狀態轉移:
如果最後那塊放2*1的:f(n) = f(n-1) + f(n-2)
如果最後那塊放3*1的:f(n) = f(n-3) + f(n-4) + ... + f(0),由於對稱的關系,要乘2
綜合起來: f(n) = f(n-1) + f(n-2) + 2 * (f(n-3) + f(n-4) +...+ f(0))
邊界狀態:f(0) = 1; f(1) = 1; f(2) = 2;
*/

//top down
int dp[101] = {0};
void init()
{
    dp[0] = dp[1] = 1;
    dp[2] = 2;
}
int fun(int n)
{
    init();
    if(dp[n] != 0) return dp[n];
    dp[n] = 2 * fun(n-1) + fun(n-3);
    return dp[n];
}

//bottom up
int dp2[101] = {};

void build()
{
    dp2[0] = dp2[1] = 1;
    dp2[2] = 2;

    for(int i = 3 ; i < 101 ; i++)
        dp2[i] = 2 * dp2[i-1] + dp2[i-3];
}

int fun2(int n)
{
    return dp2[n];
}

int main()
{
    int a;
    srand((int)time(NULL));
    a = rand()%100;

    build();
    cout << fun(a) << endl;
    cout << fun2(a) << endl;

    return 0;
}

4.找到不相鄰的數的最大和

/*
@param:dp 入門
@writer:pprp
@declare:最經典最簡單的dp,給你一個正整數陣列,
從裏邊取出不相鄰的數,問你取出數字和最大為多少?
@begin:1:05
@end:11:25
@error:應該是從1開始不是從0開始
@date:2017/9/1
*/

#include <bits/stdc++.h>

using namespace std;

/*
狀態分析:
f(n)代表的是當前取了這個位置以後的最大數字和
狀態轉移:
f(n) = max(f(n-2),f(n-3) + arr[n];
邊界狀態:f(0) = 0, f(1) = arr[1], f[2] = max(arr[1],arr[2])
*/

//top down
int arr[101] = {};
int dp[110] = {};

void init()
{
    dp[0] = 0;
    dp[1] = arr[1];
    dp[2] = max(arr[1],arr[2]);
    dp[3] = max(arr[1]+arr[3],arr[2]);
}

int fun(int n)
{
    init();
    if(dp[n] != 0)
        return dp[n];
    dp[n] = max(fun(n-2),fun(n-3)) + arr[n];
    return dp[n];
}

int dp2[110] = {};
//bottom up
void build()
{
    dp2[0] = 0;
    dp2[1] = arr[1];
    dp2[2] = max(arr[1],arr[2]);

    for(int i = 3; i < 101 ; i++)
    {
        dp2[i] = max(dp2[i-2],dp2[i-3]) + arr[i];
    }
}

int fun2(int n)
{
    return dp2[n];
}
int main()
{
    freopen("in.txt","r",stdin);
    int n;
    cin >> n;
    for(int i = 1 ; i <= n ; i++)
        cin >> arr[i];

    init();
    build();

    cout << max(fun(4),fun(5)) << endl;
    cout << max(fun2(4),fun2(5)) << endl;
    return 0;
}

總結:

狀態確定很重要,利用對稱的關系分析可以簡化,

動態規劃是一個走一步算一步的算法過程,不要全局的去分析,否則越分析越亂,按照每一步的走向來確定狀態轉移方程

做的多了動態規劃才能有靈感

動態規劃入門(一)