暴力动态规划

动态规划的本质:递归
递归:将原问题规模n变为n-1,逐次击破

动态规划问题共性:
1.一般都是求最优、最大、最小问题
2.离散问题,容易设计状态
3.有最优子结构,由N-1可以推导出N,无后效性

四个步骤:
设计暴力算法,找到冗余
设计并存储状态(一维,二维,三位数组,甚至用Map)(去冗余)
递归式(状态转移方程)
自底向上计算最优解(编程方式)

斐波拉契数列

//递归写法,至顶向下
int fib(int n) {
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    else
        return fib(n - 1) + fib(n - 2);
}

//记忆化搜索写法,至顶向下
int *f;
int fib(int n) {
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    if (f[n]>0)
        return f[n];
    f[n] = fib(n - 1) + fib(n - 2);
    return f[n];
}
int fun(int n) {
    f = new int[n + 1];
    memset(f, 0, sizeof(int)*(n + 1));
    return fib(n);
}

//递推写法,至底向上
int fib1(int n) {
    if (n == 0)
        return 0;
    else if (n == 1)
        return 1;
    int *f = new int[n+1];
    f[0] = 0;
    f[1] = 1;
    for (int i = 2; i <= n; i++) {
        f[i] = f[i - 1] + f[i - 2];
    }
    return f[n];
}

Leetcode 198.House Robber

//递归写法
int fun(int n, vector<int>& nums) {
    if (n < 0) return 0;
    return max(fun(n - 1, nums), fun(n - 2, nums) + nums[n]);         
}
int rob(vector<int>& nums) {
    int n = nums.size();
    return fun(n - 1, nums);
} 
//记忆化搜索写法
class Solution {
public:
    int fun(int n, vector<int>& nums) {
        if (n < 0) return 0;
        if(res[n] >= 0) {
            return res[n];
        }
        res[n] = max(fun(n - 1, nums), fun(n - 2, nums) + nums[n]);
        return res[n];
    }
    int rob(vector<int>& nums) {
        int n = nums.size();
        res = new int[n];
        for(int i = 0; i < n; i++) {
            res[i] = -1;
        }
        return fun(n - 1, nums);
    }
private:
    int *res;    
};
//递归改递推
int rob(vector<int>& nums) {
    int size = nums.size();
    if(size == 0) return 0;
    else if(size ==1) return nums[0];
    int *res = new int[size];
    res[0] = nums[0];
    res[1] = max(nums[0], nums[1]);
    for(int i = 2; i < size; i++) {
        res[i] = max(res[i - 1], res[i - 2] + nums[i]); 
    }
    return res[size - 1];
}  

最长递增子序列
arr:2 1 5 3 6 4 8 9 7
dp :1 1 2 2 3 3 4 5 4

dp[i]表示在必须以arr[i]这个数结尾的情况下,arr[0..i]中的最大递增子序列长度
dp[0] = 1;
dp[i] = max{dp[i]+1(0<=j<i, arr[j]<arr[i])}
int LongestIncrementSubsequence(vector<int>& nums) {
    int n = nums.size();
    if (n < 1) return 0;

    vector<int> dp(n, 1);

    for (int i = 1; i < n; i++) {
        for (int j = 0; j < i; j++) {
            if (nums[i] > nums[j])
                dp[i] = max(dp[i], dp[j] + 1);
        }
    }
    return dp[n - 1];
}

Coin Change问题

//递归写法
class Solution {
public:
    int visit(vector<int>& coins, int i, int total) {
        if (total == 0) return 0;
        if (total < 0 || i < 0) return maxValue;

        return min(visit(coins, i, total - coins[i]) + 1, visit(coins, i - 1, total));
    }

    int coinChange(vector<int>& coins, int amount) {
        int size = coins.size();
        if (size < 1) return -1;
        maxValue = 999999;
        int res = visit(coins, size - 1, amount);
        if (res < maxValue)
            return res;
        else
            return -1;
    }
private:
    int maxValue;
};

//记忆化搜索写法
class Solution {
public:
    int visit(vector<int>& coins, int i, int total) {
        if (total == 0) return 0;
        if (total < 0 || i < 0) return maxValue;
        //if (coins[i] > total) return visit(coins, i -1, total);
        if (f[i][total] > 0)
            return f[i][total];
        f[i][total] = min(visit(coins, i, total - coins[i]) + 1, visit(coins, i - 1, total));
        return f[i][total];
    }

    int coinChange(vector<int>& coins, int amount) {
        int size = coins.size();
        if (size < 1) return -1;
        maxValue = 999999;
        f = new int*[size];
        for(int i = 0; i < size; i++) {
            f[i] = new int[amount + 1];
            memset(f[i], 0, sizeof(int)*(amount + 1));
        }
        int res = visit(coins, size - 1, amount);
        if (res < maxValue)
            return res;
        else
            return -1;
    }
private:
    int maxValue;
    int **f;
};

//递推写法
int coinChange(vector<int>& coins, int amount) {
    int size = coins.size();
    if (size < 1) return -1;
    int maxValue = 999999;
    int **f = new int*[size];
    for (int i = 0; i < size; i++) {
        f[i] = new int[amount + 1];
        for (int j = 0; j < amount + 1; j++) {
            f[i][j] = maxValue;
        }
    }
    for (int j = 0; j < amount + 1; j++) {
        if (j % coins[0] == 0) {
            f[0][j] = j / coins[0];
        }
    }
    for (int i = 1; i < size; i++) {
        f[i][0] = 0;
        for (int j = 1; j <= amount; j++) {
            if (j - coins[i] < 0)
                f[i][j] = f[i - 1][j];
            else
                f[i][j] = min(f[i][j - coins[i]] + 1, f[i - 1][j]);
        }
    }

    return f[size - 1][amount] < maxValue ? f[size - 1][amount] : -1;

}
//递推加滚动数组
int coinChange(vector<int>& coins, int amount) {
    int size = coins.size();
    if (size < 1) return -1;
    int maxValue = 999999;
    int **f = new int*[2];
    for (int i = 0; i < 2; i++) {
        f[i] = new int[amount + 1];
        for (int j = 0; j < amount + 1; j++) {
            f[i][j] = maxValue;
        }
    }
    for (int j = 0; j < amount + 1; j++) {
        if (j % coins[0] == 0) {
            f[0][j] = j / coins[0];
        }
    }
    for (int i = 1; i < size; i++) {
        f[i % 2][0] = 0;
        for (int j = 1; j <= amount; j++) {
            if (j - coins[i] < 0)
                f[i % 2][j] = f[(i - 1) % 2][j];
            else
                f[i % 2][j] = min(f[i % 2][j - coins[i]] + 1, f[(i - 1) % 2][j]);
        }
    }

    return f[(size - 1) % 2][amount] < maxValue ? f[(size - 1) % 2][amount] : -1;

}
//dp[i]表示拼凑出i元的硬币最小数
int coinChange(vector<int>& coins, int amount) {
    int size = coins.size();
    if (size < 1) return -1;
    int maxValue = amount + 1;
    vector<int> dp(maxValue, maxValue);
    dp[0] = 0;
    for (int i = 1; i < maxValue; i++) {
        for (int j = 0; j < size; j++) {
            if (i - coins[j] >= 0)
                dp[i] = min(dp[i], dp[i - coins[j]] + 1);
        }
    }

    return dp[amount] >= maxValue ? -1 : dp[amount];

}

最长公共子序列

//递归解法
int visit1(string s1, string s2, int i, int j) {

    if (i >= s1.length() || j >= s2.length()) return 0;
    if (s1[i] == s2[j])
        return visit1(s1, s2, i + 1, j + 1) + 1;
    else {
        return max(
            visit1(s1, s2, i + 1, j),
            visit1(s1, s2, i, j + 1)
            );
    }
}
int visit2(string s1, string s2, int i, int j) {
    if (i < 0 || j < 0) return 0;
    if (s1[i] == s2[j])
        return visit2(s1, s2, i - 1, j - 1) + 1;
    else
        return max(
            visit2(s1, s2, i - 1, j),
            visit2(s1, s2, i, j - 1)
        );
}

//递推解法
int LongestCommonSebsequence(string s1, string s2) {
    int len1 = s1.length();
    int len2 = s2.length();
    if (len1 < 1 || len2 < 1) return 0;

    vector<vector<int>> dp(len1, vector<int>(len2));
    dp[0][0] = 0;
    for (int i = 0; i < len1; i++) {
        int flag = false;
        if (s1[i] == s2[0]) {
            flag = true;
        }
        if (flag)
            dp[i][0] = 1;
        else
            dp[i][0] = 0;
    }
    for (int j = 0; j < len2; j++) {
        int flag = false;
        if (s1[0] == s2[j]) {
            flag = true;
        }
        if (flag)
            dp[0][j] = 1;
        else
            dp[0][j] = 0;
    }

    for (int i = 1; i < s1.length(); i++) {
        for (int j = 1; j < s2.length(); j++) {
            if (s1[i] == s2[j])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
        }
    }

    return dp[len1 -1][len2 - 1];
}
int LongestCommonSubsequence(string str1, string str2) {
    int m = str1.length();
    int n = str2.length();
    if (m == 0 || n == 0)
        return 0;

    vector<vector<int>> dp(m+1, vector<int>(n+1, 0));

    for (int i = 1; i < m + 1; i++) {
        for (int j = 1; j < n + 1; j++) {
            if (str1[i-1] == str2[j-1])//注意-1
                dp[i][j] = dp[i - 1][j - 1] + 1;
            else
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
        }
    }
    return dp[m][n];
}

leetcode 72 最小编辑代价

int minDistance(string word1, string word2) {
    int n1 = word1.length();
    int n2 = word2.length();
    vector<vector<int>> dp(n1 + 1, vector<int>(n2 + 1, 0));
    for(int i = 0; i <= n1; i++ ) {
        dp[i][0] = i;
    }
    for(int j = 0; j <= n2; j++ ) {
        dp[0][j] = j;
    }        

    for(int i = 1; i <= n1; i++ ) {
        for(int j = 1; j <= n2; j++ ) {
            if (word1[i-1] == word2[j-1])
                dp[i][j] = dp[i - 1][j - 1];
            else
                dp[i][j] = min(dp[i - 1][j - 1] + 1, min(dp[i][j - 1] + 1, dp[i - 1][j] + 1));
        } 
    }
    return dp[n1][n2];
}

步骤:
1.根据提议找到递推式
2.决定动态数组大小m * n 或(m + 1)*(n + 1)
3.根据实际条件写出初始值
4.优化:滚动数组、二维改一维

猜你喜欢

转载自blog.csdn.net/ts1130/article/details/78959524