算法:动态规划

动态规划的规则

1.状态定义

2.固定住不确定的因素(例如1.确定连续子数组,我们可以选择固定子数组的末尾元素2.台阶确定最后一阶台阶),是得状态转移变得容易

3.求解过程中每个子问题只求解一次,无后效性。

一 斐波那契数列

1.1 题目描述

写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:

F(0) = 0,   F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:1
示例 2:

输入:n = 5
输出:5

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/fei-bo-na-qi-shu-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

1.2 解题思路以及代码 

对于斐波拉契数,我们可以选择使用递归方法,但是会超时。使用动态规划的实践复杂度为O(n).

 递归代码如下:

 public static int Fib(int n)
    {
        if (n == 0)
        {
            return 0;
        }else if (n == 1)
        {
            return 1;
        }
        else
        {
            return Fib(n - 1) + Fib(n - 2);
        }

    }

 动态规划代码如下:

public static int Fib1(int n)
    {
        if (n == 0)
        {
            return 0;
        }
        else if (n == 1)
        {
            return 1;
        }
        int a = 0;
        int b = 1;
        int c=0;
       for(int i = 2; i <= n; i++)
        {
            c = (a + b) % 1000000007;          
            a = b;
            b = c;
                   
        }
        return c;

    }

 动态规划对于除余进行了处理。其他的均是动态规划思想,但是不是用数组去明确表示,而是使用了abc三个变量去对数组进行了表示。

动态规划的表格如下:

0 1 2 3 4 5
dp 0 1 1 2 3 5

对比此题的递归和动态规划,我们发现递归是从后往前(是栈),动态规划是从前往后(从小到大)。

二  青蛙跳台阶问题

2.1 题目描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入:n = 2
输出:2
示例 2:

输入:n = 7
输出:21
示例 3:

输入:n = 0
输出:1

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/qing-wa-tiao-tai-jie-wen-ti-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2.2 思路以及代码

public int NumWays(int n) {
        if (n == 0)
        {
            return 1;
        }else if (n == 1)
        {
            return 1;
        }
       int a = 1;
        int b = 1;
        int c=0;
       for(int i = 2; i <= n; i++)
        {
            c = (a + b)% 1000000007;
            a = b;
            b = c;
                   
        }
        return c;

    }

此题跟上一题比较,初始条件变更而已。

三 股票的最大利润

3.1 题目描述

假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?

示例 1:

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/gu-piao-de-zui-da-li-run-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

3.2 思路以及代码

7 1 5 3 6 4
min 7 1 1 1 1 1

maxProfit

7-7=0 0 4 4 5 5

 dp[i],确定的状态是以i天为结尾的时候,我们可以确定第i天卖股票,卖出价格,以及i天之前最低价格买入的价格。

min表示i天结尾的时候,最低买入价格。maxProfit表示最大利润。

代码为:

public static int MaxProfit(int[] prices)
    {
        int minPrice = prices[0];
        int maxFrofix = 0;
        for(int i = 0; i < prices.Length; i++)
        {
            if (minPrice > prices[i])
            {
                minPrice = prices[i];
            }
            maxFrofix = Math.Max(maxFrofix, prices[i] - minPrice);
        }
        Console.WriteLine(maxFrofix);
        return maxFrofix;
    }

 在main方法中使用此语句:

MaxProfit(new int[] {7,1,5,3,6,4 });

结果为

5

四 连续子数组的最大和

4.1 题目描述

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。

要求时间复杂度为O(n)。

示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

4.2 思路以及代码

nums -2 1 -3 4 -1 2 1 -5 4
i为尾最大子数和 -2 1 -2 4 3 5 6 1 5
public static int MaxSubArray(int[] nums)
    {
        int MaxSubArraySum = nums[0];
        int[] dp = new int[nums.Length];
        dp[0] = nums[0];
        if (nums.Length == 1)
        {
            return MaxSubArraySum;
        }
        for(int i = 1; i < nums.Length; i++)
        {
            dp[i] = Math.Max(dp[i - 1] + nums[i], nums[i]);
            MaxSubArraySum = Math.Max(dp[i], MaxSubArraySum);
        }
        Console.WriteLine(MaxSubArraySum);
        return MaxSubArraySum;
    }

主函数使用如下: 

MaxSubArray(new int[] { -2, 1, -3, 4, -1, 2, 1, -5, 4 });

结果为

6

你可能感兴趣的:(c#,算法机试,算法,动态规划)