一维动态规划

这篇具有很好参考价值的文章主要介绍了一维动态规划。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一维动态规划

509. 斐波那契数

int *dp;

// 自顶向下记忆化搜索,时间复杂度O(n)
int recursive(int n) {
    if (n == 0)return 0;
    if (n == 1) return 1;
    // 若之前计算过就直接返回
    if (dp[n] != -1) return dp[n];
    dp[n] = recursive(n - 2) + recursive(n - 1);
    return dp[n];
}

int fib(int n) {
    dp = (int *) malloc(sizeof(int) * (n + 1));
    memset(dp, -1, sizeof(int) * (n + 1));
    return recursive(n);
}
// 自下而上,时间复杂度O(n)
int fib(int n) {
    int dp[31];
    dp[0] = 0;
    dp[1] = 1;
    for (int i = 2; i <= n; ++i)
        // 状态转移方程
        dp[i] = dp[i - 2] + dp[i - 1];
    return dp[n];
}
// 状态压缩,时间复杂度O(n)
int fib(int n) {
    if (n < 2) return n;
    int left = 0;
    int mid = 1;
    int right;
    
    for (int i = 2; i <= n; ++i) {
        right = left + mid;
        left = mid;
        mid = right;
    }

    return right;
}
// todo 矩阵快速幂,时间复杂度O(logn)
// 代入通项公式
int fib(int n) {
    double sqrt5 = sqrt(5);
    double fibN = pow((1 + sqrt5) / 2, n) - pow((1 - sqrt5) / 2, n);
    // 四舍五入成正数
    return round(fibN / sqrt5);
}
// 打表

983. 最低票价

// 每种方案对应的通行天数
int durations[3] = {1, 7, 30};

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

// 返回从第day[i]天开始往后的行程全部完成所需的最小花费
int recursive(int *days, int daysSize, int *costs, int curIndex) {
    if (curIndex == daysSize) return 0;
    int res = 0x7fffffff;
    // 一共三种方案
    for (int i = 0; i < 3; ++i) {
        // day[index]为下一个需要买票的日子
        int index = curIndex;
        // 可以连续通行的最后一天的后一天
        int nextDay = days[index] + durations[i];
        while (index < daysSize && days[index] < nextDay)
            index++;
        // 记录最小值
        res = min(res, costs[i] + recursive(days, daysSize, costs, index));
    }
    return res;
}

// 暴力法超时,时间复杂度O(3^n)
int mincostTickets(int *days, int daysSize, int *costs, int costsSize) {
    return recursive(days, daysSize, costs, 0);
}
// 每种方案对应的通行天数
int durations[3] = {1, 7, 30};
int *dp;

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

// 返回从第day[i]天开始往后的行程全部完成所需的最小花费
int recursive(int *days, int daysSize, int *costs, int curIndex) {
    if (curIndex == daysSize) return 0;
    if (dp[curIndex] != 0x7fffffff) return dp[curIndex];
    int res = 0x7fffffff;
    // 一共三种方案
    for (int i = 0; i < 3; ++i) {
        // day[index]为下一个需要买票的日子
        int index = curIndex;
        // 可以连续通行的最后一天的后一天
        int nextDay = days[index] + durations[i];
        while (index < daysSize && days[index] < nextDay)
            index++;
        // 记录最小值
        res = min(res, costs[i] + recursive(days, daysSize, costs, index));
    }
    dp[curIndex] = res;
    return res;
}

// 记忆化搜索,时间复杂度O(3n)=O(n)
int mincostTickets(int *days, int daysSize, int *costs, int costsSize) {
    dp = (int *) malloc(sizeof(int) * daysSize);
    for (int i = 0; i < daysSize; ++i) dp[i] = 0x7fffffff;
    return recursive(days, daysSize, costs, 0);
}
int min(int a, int b) {
    return a > b ? b : a;
}

// 自下而上
int mincostTickets(int *days, int daysSize, int *costs, int costsSize) {
    // 每种方案对应的通行天数
    int durations[3] = {1, 7, 30};
    int dp[daysSize + 1];
    for (int i = 0; i < daysSize; ++i) dp[i] = 0x7fffffff;
    dp[daysSize] = 0;
    for (int curIndex = daysSize - 1; curIndex >= 0; curIndex--) {
        // 一共三种方案
        for (int i = 0; i < 3; ++i) {
            // day[index]为下一个需要买票的日子
            int index = curIndex;
            // 可以连续通行的最后一天的后一天
            int nextDay = days[index] + durations[i];
            while (index < daysSize && days[index] < nextDay)
                index++;
            // 记录最小值
            dp[curIndex] = min(dp[curIndex], dp[index] + costs[i]);
        }
    }
    return dp[0];
}

91. 解码方法

int len;

// 返回从curIndex位置往后的字符串有多少种解码方式
int recursive(char *s, int curIndex) {
    // 返回1表示到末尾结束了,之前的解码算是一种方案
    if (curIndex == len) return 1;
    int res;
    // 当前位置是0,无法解码
    if (s[curIndex] == '0') {
        res = 0;
    } else {
        // i位置可以对应一个字符
        res = recursive(s, curIndex + 1);
        int val = (s[curIndex] - '0') * 10 + (s[curIndex + 1] - '0');
        if (curIndex + 1 < len && val <= 26)
            // i和i+1位置合在一起也能构成一个字母
            res += recursive(s, curIndex + 2);
    }
    return res;
}

// 暴力法超时,O(2^n)
int numDecodings(char *s) {
    len = strlen(s);
    return recursive(s, 0);
}
int len;
int *dp;

// 返回从curIndex位置往后的字符串有多少种解码方式
int recursive(char *s, int curIndex) {
    // 返回1表示到末尾结束了,之前的解码算是一种方案
    if (curIndex == len) return 1;
    if (dp[curIndex] != -1) return dp[curIndex];
    int res;
    // 当前位置是0,无法解码
    if (s[curIndex] == '0') {
        res = 0;
    } else {
        // curIndex位置可以对应一个字符
        res = recursive(s, curIndex + 1);
        if (curIndex + 1 < len && (s[curIndex] - '0') * 10 + (s[curIndex + 1] - '0') <= 26)
            // curIndex和curIndex+1位置合在一起也能构成一个字母
            res += recursive(s, curIndex + 2);
    }
    dp[curIndex] = res;
    return res;
}

// 记忆化搜索,O(n)
int numDecodings(char *s) {
    len = strlen(s);
    dp = (int *) malloc(sizeof(int) * len);
    memset(dp, -1, sizeof(int) * len);
    return recursive(s, 0);
}
int numDecodings(char *s) {
    int len = strlen(s);;
    int *dp = (int *) malloc(sizeof(int) * (len + 1));
    dp[len] = 1;
    for (int curIndex = len - 1; curIndex >= 0; curIndex--) {
        if (s[curIndex] == '0') {
            // 当前位置是0,无法解码
            dp[curIndex] = 0;
        } else {
            // curIndex位置可以对应一个字符
            dp[curIndex] = dp[curIndex + 1];
            if (curIndex + 1 < len && (s[curIndex] - '0') * 10 + (s[curIndex + 1] - '0') <= 26)
                // curIndex和curIndex+1位置合在一起也能构成一个字母
                dp[curIndex] += dp[curIndex + 2];
        }
    }

    return dp[0];
}
// 状态压缩
// 类似有条件的斐波那契数列
int numDecodings(char *s) {
    int len = strlen(s);;
    int left, mid = 1, right;
    for (int curIndex = len - 1; curIndex >= 0; curIndex--) {
        if (s[curIndex] == '0') {
            // 当前位置是0,无法解码
            left = 0;
        } else {
            // curIndex位置可以对应一个字符
            left = mid;
            if (curIndex + 1 < len && (s[curIndex] - '0') * 10 + (s[curIndex + 1] - '0') <= 26)
                // curIndex和curIndex+1位置合在一起也能构成一个字母
                left += right;
        }
        right = mid;
        mid = left;
    }

    return left;
}

639. 解码方法 II

int len;
const int mod = 1e9 + 7;

// 返回从curIndex位置往后有多少种有效转化
int recursive(char *s, int curIndex) {
    if (curIndex == len) return 1;
    // 1 转不了
    if (s[curIndex] == '0') return 0;
    // 2 curIndex单独转换。当前位置是*,则有9种转法;是正常的非0数,则有一种转法
    int res = (s[curIndex] == '*' ? 9 : 1) * recursive(s, curIndex + 1);
    // 3 curIndex和curIndex+1一起转
    if (curIndex + 1 < len) {
        // 存在curIndex+1的位置
        // 根据是否是*,分为4种情况
        if (s[curIndex] != '*') {
            if (s[curIndex + 1] != '*') {
                // 3.1 num num
                // 能转成1~26,才算一种
                if ((s[curIndex] - '0') * 10 + s[curIndex + 1] - '0' <= 26)
                    res += recursive(s, curIndex + 2);
            } else {
                // 3.2 num *
                if (s[curIndex] == '1')
                    // 11~19,9种转发
                    res += 9 * recursive(s, curIndex + 2);
                if (s[curIndex] == '2')
                    // 21~26,6种转发
                    res += 6 * recursive(s, curIndex + 2);
            }
        } else {
            if (s[curIndex + 1] != '*') {
                // 3.3 * num
                if (s[curIndex + 1] <= '6')
                    // 在num<=6时,*可以是1或2,1num、2num,2种
                    res += 2 * recursive(s, curIndex + 2);
                else
                    // 在num>6时,*只能为1,1种
                    res += recursive(s, curIndex + 2);
            } else {
                // 3.4 * *
                // 11~19 21~26 15种
                res += 15 * recursive(s, curIndex + 2);
            }
        }
    }
    return res % mod;
}

// 暴力法超时
int numDecodings(char *s) {
    len = strlen(s);
    return recursive(s, 0);
}
int len;
const int mod = 1e9 + 7;
int *dp;

// 返回从curIndex位置往后有多少种有效转化
long long recursive(char *s, int curIndex) {
    if (curIndex == len) return 1;
    // 1 转不了
    if (s[curIndex] == '0') return 0;
    // 从备忘录种返回
    if (dp[curIndex] != -1) return dp[curIndex];
    // 2 curIndex单独转换。当前位置是*,则有9种转法;是正常的非0数,则有一种转法
    long long res = (s[curIndex] == '*' ? 9 : 1) * recursive(s, curIndex + 1);
    // 3 curIndex和curIndex+1一起转
    if (curIndex + 1 < len) {
        // 存在curIndex+1的位置
        // 根据是否是*,分为4种情况
        if (s[curIndex] != '*') {
            if (s[curIndex + 1] != '*') {
                // 3.1 num num
                // 能转成1~26,才算一种
                if ((s[curIndex] - '0') * 10 + s[curIndex + 1] - '0' <= 26)
                    res += recursive(s, curIndex + 2);
            } else {
                // 3.2 num *
                if (s[curIndex] == '1')
                    // 11~19,9种转发
                    res += 9 * recursive(s, curIndex + 2);
                if (s[curIndex] == '2')
                    // 21~26,6种转发
                    res += 6 * recursive(s, curIndex + 2);
            }
        } else {
            if (s[curIndex + 1] != '*') {
                // 3.3 * num
                if (s[curIndex + 1] <= '6')
                    // 在num<=6时,*可以是1或2,1num、2num,2种
                    res += 2 * recursive(s, curIndex + 2);
                else
                    // 在num>6时,*只能为1,1种
                    res += recursive(s, curIndex + 2);
            } else {
                // 3.4 * *
                // 11~19 21~26 15种
                res += 15 * recursive(s, curIndex + 2);
            }
        }
    }
    dp[curIndex] = res % mod;
    return dp[curIndex];
}

// 记忆化搜索
int numDecodings(char *s) {
    len = strlen(s);
    dp = (int *) malloc(sizeof(int) * len);
    memset(dp, -1, sizeof(int) * len);
    return recursive(s, 0);
}
// 自下而上,严格位置依赖
int numDecodings(char *s) {
    const int mod = 1e9 + 7;
    int len = strlen(s);
    // dp[curIndex]返回从curIndex位置往后有多少种有效转化
    long long *dp = (long long *) malloc(sizeof(long long) * (len + 1));
    dp[len] = 1;

    for (int curIndex = len - 1; curIndex >= 0; curIndex--) {
        // 1 转不了
        if (s[curIndex] == '0') {
            dp[curIndex] = 0;
            continue;
        }
        // 2 curIndex单独转换。当前位置是*,则有9种转法;是正常的非0数,则有一种转法
        dp[curIndex] = (s[curIndex] == '*' ? 9 : 1) * dp[curIndex + 1];
        // 3 curIndex和curIndex+1一起转
        if (curIndex + 1 < len) {
            // 存在curIndex+1的位置
            // 根据是否是*,分为4种情况
            if (s[curIndex] != '*') {
                if (s[curIndex + 1] != '*') {
                    // 3.1 num num
                    // 能转成1~26,才算一种
                    if ((s[curIndex] - '0') * 10 + s[curIndex + 1] - '0' <= 26)
                        dp[curIndex] += dp[curIndex + 2];
                } else {
                    // 3.2 num *
                    if (s[curIndex] == '1')
                        // 11~19,9种转发
                        dp[curIndex] += 9 * dp[curIndex + 2];
                    if (s[curIndex] == '2')
                        // 21~26,6种转发
                        dp[curIndex] += 6 * dp[curIndex + 2];
                }
            } else {
                if (s[curIndex + 1] != '*') {
                    // 3.3 * num
                    if (s[curIndex + 1] <= '6')
                        // 在num<=6时,*可以是1或2,1num、2num,2种
                        dp[curIndex] += 2 * dp[curIndex + 2];
                    else
                        // 在num>6时,*只能为1,1种
                        dp[curIndex] += dp[curIndex + 2];
                } else {
                    // 3.4 * *
                    // 11~19 21~26 15种
                    dp[curIndex] += 15 * dp[curIndex + 2];
                }
            }
        }
        dp[curIndex] %= mod;
    }

    return (int) dp[0];
}
// 自下而上,严格位置依赖+状态压缩
int numDecodings(char *s) {
    const int mod = 1e9 + 7;
    int len = strlen(s);
    // left返回从curIndex位置往后有多少种有效转化
    long long left, mid = 1, right;

    for (int curIndex = len - 1; curIndex >= 0; curIndex--) {
        // 1 转不了
        if (s[curIndex] == '0') {
            left = 0;
            right = mid;
            mid = left;
            continue;
        }
        // 2 curIndex单独转换。当前位置是*,则有9种转法;是正常的非0数,则有一种转法
        left = (s[curIndex] == '*' ? 9 : 1) * mid;
        // 3 curIndex和curIndex+1一起转
        if (curIndex + 1 < len) {
            // 存在curIndex+1的位置
            // 根据是否是*,分为4种情况
            if (s[curIndex] != '*') {
                if (s[curIndex + 1] != '*') {
                    // 3.1 num num
                    // 能转成1~26,才算一种
                    if ((s[curIndex] - '0') * 10 + s[curIndex + 1] - '0' <= 26)
                        left += right;
                } else {
                    // 3.2 num *
                    if (s[curIndex] == '1')
                        // 11~19,9种转发
                        left += 9 * right;
                    if (s[curIndex] == '2')
                        // 21~26,6种转发
                        left += 6 * right;
                }
            } else {
                if (s[curIndex + 1] != '*') {
                    // 3.3 * num
                    if (s[curIndex + 1] <= '6')
                        // 在num<=6时,*可以是1或2,1num、2num,2种
                        left += 2 * right;
                    else
                        // 在num>6时,*只能为1,1种
                        left += right;
                } else {
                    // 3.4 * *
                    // 11~19 21~26 15种
                    left += 15 * right;
                }
            }
        }
        left %= mod;
        right = mid;
        mid = left;
    }

    return (int) left;
}

263. 丑数

bool isUgly(int n) {
    if (n <= 0) return false;
    // 如果n能被2整除,就除掉一个2
    while (n % 2 == 0) n /= 2;
    while (n % 3 == 0) n /= 3;
    while (n % 5 == 0) n /= 5;
    return n == 1;
}

264. 丑数 II

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

int min3(int a, int b, int c) {
    return min(min(a, b), c);
}

int nthUglyNumber(int n) {
    // dp[i]为第i个丑数
    int dp[n + 1];
    dp[1] = 1;
    int i2 = 1, i3 = 1, i5 = 1;
    int curIndex = 2;

    while (curIndex <= n) {
        int val2 = dp[i2] * 2;
        int val3 = dp[i3] * 3;
        int val5 = dp[i5] * 5;
        int m = min3(val2, val3, val5);
        // 没有else,因为可能有多个指针同时往后走
        if (m == val2) i2++;
        if (m == val3) i3++;
        if (m == val5) i5++;

        dp[curIndex++] = m;
    }

    return dp[n];
}

32. 最长有效括号

int longestValidParentheses(char *s) {
    int len = strlen(s);
    if (len <= 1) return 0;

    // dp[i]表示以s[i]结尾的最长有效括号的长度
    int dp[len];
    for (int i = 0; i < len; ++i) dp[i] = 0;
    int max = 0;
    for (int i = 1; i < len; ++i) {
        // 以'('结尾,无法形成有效括号,以')'结尾才有可能
        if (s[i] == ')') {
            // 以s[i-1]为结尾的最长有效括号的开头的左边一个字符的下标
            int index = i - 1 - dp[i - 1];
            if (index >= 0 && s[index] == '(')
                dp[i] = dp[i - 1] + 2 + (index > 0 ? dp[index - 1] : 0);
        }
        // 记录最大
        if (dp[i] > max) max = dp[i];
    }
    return max;
}

467. 环绕字符串中唯一的子字符串

int findSubstringInWraproundString(char *s) {
    int len = strlen(s);
    int str[len];
    // 转成对应的0~26
    for (int i = 0; i < len; ++i)
        str[i] = s[i] - 'a';

    // dp[i]表示以i+'a'结尾的字符向左,按照base串规则的最大延伸长度
    // 也代表了以i+'a'结尾,符合条件的可能总数
    int dp[26] = {0};
    dp[str[0]] = 1;
    int pre, cur, count = 1;
    for (int i = 1; i < len; ++i) {
        cur = str[i];
        pre = str[i - 1];
        if ((pre + 1) % 26 == cur)
            count++;
        else
            count = 1;
        if (count > dp[cur])
            dp[cur] = count;
    }
    int res = 0;
    for (int i = 0; i < 26; ++i)
        res += dp[i];
    return res;
}

940. 不同的子序列 II

// todo
int distinctSubseqII(char *s) {
    int len = strlen(s);
    int mod = 1e9 + 7;
    // 总数(包括了空集)
    int all = 1;
    // 新增的个数
    int newAdd;
    // count[s[i] - 'a']表示以s[i]结尾的子序列个数
    int count[26] = {0};
    for (int i = 0; i < len; ++i) {
        newAdd = (all - count[s[i] - 'a'] + mod) % mod;
        all = (all + newAdd) % mod;
        count[s[i] - 'a'] = (count[s[i] - 'a'] + newAdd) % mod;
    }
    return (all - 1 + mod) % mod;
}

文章来源地址https://www.toymoban.com/news/detail-816383.html

到了这里,关于一维动态规划的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态规划02-斐波那契类型二

    给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。 真题点击此处:746.使用最小花费爬楼

    2024年01月16日
    浏览(40)
  • 【算法学习】斐波那契数列模型-动态规划

            我在算法学习过程中,针对斐波那契数列模型的动态规划的例题进行了一个整理,并且根据标准且可靠一点的动态规划解题思路进行求解类似的动归问题,来达到学习和今后复习的必要。         所谓的斐波那契数列模型,即当前状态的值等于前两种状态的值之和。

    2024年02月04日
    浏览(54)
  • 算法:动态规划---斐波那契和最短路径

    从本篇开始总结的是动态规划的一些内容,动态规划是算法中非常重要的一个版块,因此也是学习算法中的一个重点,在学习动态规划前应当要把动态规划的基础知识学习一下 动态规划既然是一个新的算法,这个名字也是新名字,那么就要首先明确这个算法的名字代表什么含

    2024年01月25日
    浏览(55)
  • 动态规划之 509斐波那契数(第1道)

    题目: 斐波那契数 (通常用  表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: , ,其中 n 1 给定 n ,请计算 。 题目链接:509. 斐波那契数 - 力扣(LeetCode) 示例: 解法:

    2024年02月12日
    浏览(51)
  • 解锁动态规划:从斐波那契到高效算法

    👤作者介绍:10年大厂数据经营分析经验,现任大厂数据部门负责人。 会一些的技术:数据分析、算法、SQL、大数据相关、python 欢迎加入社区:码上找工作 作者专栏每日更新: LeetCode解锁1000题: 打怪升级之旅 python数据分析可视化:企业实战案例 备注说明:方便大家阅读,

    2024年04月15日
    浏览(38)
  • 【算法优选】 动态规划之斐波那契数列模型

    动态规划相关题目都可以参考以下五个步骤进行解答: 状态表⽰ 状态转移⽅程 初始化 填表顺序 返回值 后面题的解答思路也将按照这五个步骤进行讲解。 泰波那契序列 Tn 定义如下: T0 = 0, T1 = 1, T2 = 1, 且在 n = 0 的条件下 Tn+3 = Tn + Tn+1 + Tn+2 给你整数 n,请返回第 n 个泰波那契

    2024年02月05日
    浏览(55)
  • C++算法 —— 动态规划(1)斐波那契数列模型

    每一种算法都最好看完第一篇再去找要看的博客,因为这样会帮你梳理好思路,看接下来的博客也就更轻松了。当然,我也会尽量在写每一篇时都可以让不懂这个算法的人也能边看边理解。 动规的思路有五个步骤,且最好画图来理解细节,不要怕麻烦。当你开始画图,仔细阅

    2024年02月10日
    浏览(44)
  • 动态规划入门:斐波那契数列模型以及多状态(C++)

        动态规划(Dynamic programming,简称 DP)是一种解决多阶段决策问题的算法思想。它将问题分解为多个阶段,并通过保存中间结果来避免重复计算,从而提高效率。 动态规划的解题步骤一般分为以下几步: 思考状态表示,创建dp表(重点) 分析出状态转移方程(重点) 初始化 确定

    2024年02月11日
    浏览(42)
  • 动态规划入门篇——斐波那契数列与爬楼梯问题

           动态规划(Dynamic Programming,简称DP)是运筹学的一个分支,也是求解多阶段决策过程最优化问题的一种方法。它主要用来解决一类最优化问题,通过将复杂问题分解成若干个子问题,并综合子问题的最优解来得到原问题的最优解。动态规划的核心在于对问题的状态进

    2024年03月14日
    浏览(42)
  • 【动态规划专栏】专题一:斐波那契数列模型--------1.第N个泰波那契数

    本专栏内容为:算法学习专栏,分为优选算法专栏,贪心算法专栏,动态规划专栏以及递归,搜索与回溯算法专栏四部分。 通过本专栏的深入学习,你可以了解并掌握算法。 💓博主csdn个人主页:小小unicorn ⏩专栏分类:动态规划专栏 🚚代码仓库:小小unicorn的代码仓库🚚

    2024年02月21日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包