暴力递归与动态规划

【机器人运动问题】

参数n:表示有n个位置  1 ~ n

参数s:机器人初始位置

参数e:机器人目标位置

参数k:机器人必须走k步

机器人每次可往左或往右走,但每次只能走一步

递归调用:

// 一共1~N个位置,目标位置是E,当前位置是cur,还要走rest步
int f(int N, int E, int rest, int cur){
    if(rest == 0){
        return cur == E ? 1 : 0;
    }
    if(cur == 1){
        return f(N, E, rest - 1, 2);
    }
    if(cur == N){
        return f(N, E, rest - 1, N - 1);
    }
    return f(N, E, rest - 1, cur - 1) + f(N, E, rest - 1, cur + 1);
}

记忆化搜索:

// 一共1~N个位置,目标位置是E,当前位置是cur,还要走rest步
int f(int N, int E, int rest, int cur, vector<vector<int>>& dp){
    if(dp[rest][cur] != -1){
        return dp[rest][cur];
    }
    if(rest == 0){
        dp[rest][cur] = cur == E ? 1 : 0;
        return dp[rest][cur];
    }
    if(cur == 1){
        dp[rest][cur] = f(N, E, rest - 1, 2, dp);
    }
    else if(cur == N){
        dp[rest][cur] = f(N, E, rest - 1, N - 1, dp);
    }
    else{
        dp[rest][cur] = f(N, E, rest - 1, cur - 1, dp) + f(N, E, rest - 1, cur + 1, dp);
    }
    return dp[rest][cur];
}

int walkWays(int N, int E, int rest, int cur){
    vector<vector<int>> dp(rest + 1, vector<int> (N + 1, -1));
    return f(N, E, rest, cur, dp);
}

动态规划:

int dpWay(int N, int E, int rest, int cur){
    vector<vector<int>> dp(rest + 1, vector<int>(N + 1, 0));
    dp[0][E] = 1;
    for(int i = 1; i <= rest; i++){
        for(int j = 1; j <= N; j++){
            if(j == 1){
                dp[i][j] = dp[i - 1][j + 1];
            }
            else if(j == N){
                dp[i][j] = dp[i - 1][j - 1];
            }
            else{
                dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j + 1];
            }
        }
    }
    return dp[rest][cur];
}

【题目】

一个正整数数组,每个元素表示一枚硬币的面值,找到总面值为aim的最少个数的硬币。

如 arr = [2, 7, 3, 5, 3]   aim = 10   最少个数硬币:2(7 + 3 = 10)

int process(vector<int>& arr, int index, int rest){
    if(rest < 0){
        return -1;
    }
    if(rest == 0){
        return 0;
    }
    if(index == arr.size()){
        return -1;
    }
    int p1 = process(arr, index + 1, rest);
    int p2 = process(arr, index + 1, rest - arr[index]);
    if(p1 == -1 && p2 == -1){
        return -1;
    }
    else if(p1 == -1){
        return p2 + 1;
    }
    else if(p2 == -1){
        return p1;
    }
    else{
        return min<int>(p1, p2 + 1);
    }
}

int minCoins(vector<int>& arr, int aim){
    return process(arr, 0, aim);
}

记忆化搜索:

int process(vector<int>& arr, int index, int rest, vector<vector<int>>& dp){
    if(rest < 0){
        return -1;
    }
    if(dp[index][rest] != -2){
        return dp[index][rest];
    }
    if(rest == 0){
        dp[index][rest] = 0;
        return dp[index][rest];
    }
    if(index == arr.size()){
        dp[index][rest] = -1;
        return dp[index][rest];
    }
    int p1 = process(arr, index + 1, rest, dp);
    int p2 = process(arr, index + 1, rest - arr[index], dp);
    if(p1 == -1 && p2 == -1){
        dp[index][rest] = -1;
    }
    else if(p1 == -1){
        dp[index][rest] = p2 + 1;
    }
    else if(p2 == -1){
        dp[index][rest] = p1;
    }
    else{
        dp[index][rest] = min<int>(p1, p2 + 1);
    }
    return dp[index][rest];
}

int minCoins(vector<int>& arr, int aim){
    vector<vector<int>> dp(arr.size() + 1, vector<int>(aim + 1, -2));
    return process(arr, 0, aim, dp);
}

动态规划:

int minCoins(vector<int>& arr, int aim){
    int n = arr.size();
    vector<vector<int>> dp(n + 1, vector<int>(aim + 1, 0));
    for(int row = 0; row <= n; row++){
        dp[row][0] = 0;
    }
    for(int col = 1; col <= aim; col ++){
        dp[n][col] = -1;
    }
    for(int index = n - 1; index >= 0; index--){
        for(int rest = 1; rest <= aim; rest++){
            int p1 = dp[index + 1][rest];
            int p2 = -1;
            if(rest - arr[index] >= 0){
                p2 = dp[index + 1][rest - arr[index]];
            }
            if(p1 == -1 && p2 == -1){
                dp[index][rest] = -1;
            }
            else if(p1 == -1){
                dp[index][rest] = p2 + 1;
            }
            else if(p2 == -1){
                dp[index][rest] = p1;
            }
            else{
                dp[index][rest] = min<int>(p1, p2 + 1);
            }
        }
    }
    return dp[0][aim];
}

递归 -> 记忆化搜索 -> 严格表依赖

1.分析可变参数变化范围

2.标出要计算的终止位置

3.标出直接出答案的位置(根据basecase)

4.推普遍位置是如何依赖其他位置的

5.定出严格表依次计算的顺序

【题目】

给定一个整形数组arr,代表数值不同的纸牌排成一条线。玩家A和玩家B依次拿走每张纸牌,规定玩家A先拿,玩家B后拿,但是每个玩家每次只能拿走最左或最右的纸牌,玩家A和玩家B都绝顶聪明。返回最后获胜者的分数。

【举例】

arr = [1, 2, 100, 4]

开始时,玩家A只能拿走1或4.如果开始时玩家A拿走1,则排列变为[2, 100, 4],接下来玩家B可以拿走2或4,然后继续轮到玩家A

如果开始时玩家A拿走4,则排列变为[1, 2, 100],接下来玩家B可以拿走1或100,然后继续轮到玩家A

玩家A作为绝顶聪明的人不会先拿4,因为拿4之后,玩家B将拿走100.所以玩家A会先拿1,让排列变为[2, 100, 4],接下来玩家B不管怎么选,100都会被玩家A拿走,玩家A会获胜,分数为101,所以返回101。

int max(int a, int b){
    return a > b ? a : b;
}

int s(vector<int>& arr, int l, int j);

int f(vector<int>& arr, int l, int r){
    if(l == r){
        return arr[l];
    }
    else{
        return max(arr[l] + s(arr, l + 1, r), arr[r] + s(arr, l, r - 1));
    }
}

int s(vector<int>& arr, int l, int r){
    if(l == r){
        return 0;
    }
    else{
        return min(f(arr, l + 1, r), f(arr, l, r - 1));
    }
}

int cardsInLine(vector<int> arr){
    if(arr.empty()){
        return 0;
    }
    return max(f(arr, 0, arr.size() - 1), s(arr, 0, arr.size() - 1));
}

动态规划:

int dp(vector<int>& arr){
    if(arr.empty()){
        return 0;
    }
    vector<vector<int>> f (arr.size(), vector<int>(arr.size(), 0));
    vector<vector<int>> s (arr.size(), vector<int>(arr.size(), 0));
    for(int i = 0; i < arr.size(); i++){
        f[i][i] = arr[i];
    }
    for(int col = 1; col < arr.size(); col++){
        int i = 0;
        int j = col;
        while(i < arr.size() && j < arr.size()){
            f[i][j] = max<int>(arr[i] + s[i + 1][j], arr[j] + s[i][j - 1]);
            s[i][j] = min<int>(f[i][j - 1], f[i + 1][j]);
            i++;
            j++;
        }
    }
    return max<int>(f[0][arr.size() - 1], s[0][arr.size() - 1]);
}

【题目】

象棋棋盘十行九列,假设左下角为(0, 0),右上角为(8, 9),初始时刻棋子“马”在(0, 0),问:该棋子走step步到达(x, y)的方法有多少种?

int process(int x, int y, int step){
    if(x < 0 || x > 8 || y < 0 || y > 9){
        return 0;
    }
    if(step == 0){
        return (x == 0 && y == 0) ? 1 : 0;
    }
    return process(x - 1, y - 2, step - 1)
        + process(x - 2, y - 1, step - 1)
        + process(x + 2, y + 1, step - 1)
        + process(x + 1, y + 2, step - 1)
        + process(x + 1, y - 2, step - 1)
        + process(x + 2, y - 1, step - 1)
        + process(x - 1, y + 2, step - 1)
        + process(x - 2, y + 1, step - 1);
}

int getWays(int x, int y, int step){
    return process(x, y, step);
}

动态规划:

int getValue(vector<vector<vector<int>>>& dp, int x, int y, int h){
    if(x < 0 || x > 8 || y < 0 || y > 9){
        return 0;
    }
    return dp[x][y][h];
}

int dpWays(int x, int y, int step){
    if(x < 0 || x > 8 || y < 0 || y > 9 || step < 0){
        return 0;
    }
    vector<vector<vector<int>>> dp (9, vector<vector<int>> (10, vector<int>(step + 1, 0)));
    dp[0][0][0] = 1;
    for(int h = 1; h <= step; h++){
        for(int row = 0; row <= 8; row++){
            for(int col = 0; col <= 9; col++){
                dp[row][col][h] += getValue(dp, row - 1, col - 2, h - 1);
                dp[row][col][h] += getValue(dp, row - 2, col - 1, h - 1);
                dp[row][col][h] += getValue(dp, row + 1, col + 2, h - 1);
                dp[row][col][h] += getValue(dp, row + 2, col + 1, h - 1);
                dp[row][col][h] += getValue(dp, row - 1, col + 2, h - 1);
                dp[row][col][h] += getValue(dp, row - 2, col + 1, h - 1);
                dp[row][col][h] += getValue(dp, row + 1, col - 2, h - 1);
                dp[row][col][h] += getValue(dp, row + 2, col - 1, h - 1);
            }
        }
    }
    return dp[x][y][step];
}

【题目】

有N行M列的棋盘,左下角坐标为(0, 0),右上角坐标为(M - 1, N - 1),棋子的初始坐标为(a, b),棋子每次随机从“上下左右”中一个方向走一步,棋子需要走k步,如果棋子某一刻走出棋盘外,棋子死掉。求棋子生存的概率。

long process(int N, int M, int row, int col, int k){
    if(row < 0 || row >= N || col < 0 || col >= M){
        return 0;
    }
    if(k == 0){
        return 1;
    }
    long live = process(N, M, row - 1, col, k - 1);
    live += process(N, M, row + 1, col, k  - 1);
    live += process(N, M, row, col - 1, k - 1);
    live += process(N, M, row, col + 1, k - 1);
    return live;
}

double live(int N, int M, int row, int col, int k){
    long all = pow(4, k);
    long live = process(N, M, row, col, k);
    return (double)(live)/all;
}

动态规划:

double dplive(int N, int M, int a, int b, int k){
    vector<vector<vector<long>>> dp (N + 2, vector<vector<long>> (M + 2, vector<long> (k + 1, 0)));
    for(int row = 1; row <= N; row++){
        for(int col = 1; col <= M; col++){
            dp[row][col][0] = 1;
        }
    }
    for(int rest = 1; rest <= k; rest++){
        for(int row = 1; row <= N; row++){
            for(int col = 1; col <= M; col++){
                dp[row][col][rest] = dp[row - 1][col][rest - 1];
                dp[row][col][rest] += dp[row + 1][col][rest - 1];
                dp[row][col][rest] += dp[row][col - 1][rest - 1];
                dp[row][col][rest] += dp[row][col + 1][rest - 1];
            }
        }
    }
    long all = pow(4, k);
    long live = dp[a + 1][b + 1][k];
    return (double)(live) / all;
}

【题目】

一个整形数组arr,元素表示一种货币的面值,货币数量不限。现在要凑出的总面值为aim,求出货币组合的方法数。

int process(vector<int>& arr, int index, int rest){
    if(index == arr.size()){
        return rest == 0 ? 1 : 0;
    }
    int ways = 0;
    for(int zhang = 0; arr[index] * zhang <= rest; zhang++){
        ways += process(arr, index + 1, rest - arr[index] * zhang);
    }
    return ways;
}

int coinWays(vector<int>& arr, int aim){
    return process(arr, 0, aim);
}

动态规划:

int dpCoins(vector<int>& arr, int aim){
    if(arr.empty()){
        return 0;
    }
    int n = arr.size();
    vector<vector<int>> dp (n + 1, vector<int>(aim + 1, 0));
    dp[n][0] = 1;
    for(int index = n - 1; index >= 0; index--){
        for(int rest = 0; rest <= aim; rest++){
            int ways = 0;
            for(int zhang = 0; arr[index] * zhang <= rest; zhang++){
                ways += dp[index + 1][rest - arr[index] * zhang];
            }
            dp[index][rest] = ways;
        }
    }
    return dp[0][aim];
}

斜率优化:

int dpCoins(vector<int>& arr, int aim){
    if(arr.empty()){
        return 0;
    }
    int n = arr.size();
    vector<vector<int>> dp (n + 1, vector<int>(aim + 1, 0));
    dp[n][0] = 1;
    for(int index = n - 1; index >= 0; index--){
        for(int rest = 0; rest <= aim; rest++){
            dp[index][rest] = dp[index + 1][rest];
            if(rest - arr[index] >= 0){
                dp[index][rest] += dp[index][rest - arr[index]];
            }
        }
    }
    return dp[0][aim];
}

总结:

1.尝试版本(从左往右试,范围尝试),70%题目

2.记忆化搜索

3.严格表结构动态规划

4.更精致版本dp

可变参数最好是零维,比如整型,不要是数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芜湖高学成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值