牛客网算法教程-中级篇-第三章

1.子数组的最大累加和 -cur变量累加

题目链接
在这里插入图片描述
在这里插入图片描述
分析:
1.我们定义一个Cur变量,和Max变量。分别存储当前的值,和目前位置的最大值
2.我们去遍历数组,让Cur累加arr[i],那么这里需要判断情况了
3.如果此时Cur大于Max,则更新Max
4.如果此时Cur小于0,则代表此时前面加起来的结果是小于0的。我们来一个情况,比如说,-1,1,3,这个序列,我们是不是应该舍弃-1,而将1和3加起来
在这里插入图片描述

在这里插入图片描述

using System;
class test{
    static void Main(){
       int len=Convert.ToInt32( Console.ReadLine());
       string[] putin=Console.ReadLine().Split(' ');
       int[] arr=new int[len];
       for(int i=0;i<len;i++){
           arr[i]=Convert.ToInt32(putin[i]);
       }
        
        
       int cur=0;
       int max=0;
       
        //遍历数组
       for(int i=0;i<len;i++){
           //累加
           cur+=arr[i];
           if(cur<0){
               cur=0;
           }
           
           if(cur>max)max=cur;
       }
        
       Console.WriteLine(max);
    }
}

2.子矩阵的最大累加和

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
解析:
1.我们首先只对第一行进行求解,第一行,一个三列的数组,是不是就是一个一维数组?一维数组我们就可以用 子数组的最大累加和 来进行求解
2.然后我们对包含一二行的情况进行求解,我们发现,如果必须包含一二行的话,那么选择-5,就必须要选择9。选择2,就必须选择6。就是说,这一列必须全选。那么问题就很好解决了,我们将一列的值进行累加。我们现在一二列的累加结果就是,4,8,103 那么我们此时,就可以用 子数组的最大累加和 来进行求解
3.然后我们包含一二三行
4.此时,我们对第一行的全部情况都进行了列举,所以,我们在后面的遍历中就不需要对第一行进行考虑了,我们将主角设置为第二行
5.只包含第二行
6.只包含二三行
7.只包含第三行

在这里插入图片描述

在这里插入图片描述

using System;
class test{
    static void Main(){
        string[] putin=Console.ReadLine().Split(' ');
        int row=Convert.ToInt32( putin[0]);
        int line=Convert.ToInt32( putin[1]);
        
        
        int[,] arrs=new int[row,line];
        for(int i=0;i<row;i++){
            string[] putin2=Console.ReadLine().Split(' ');
            for(int j=0;j<line;j++){
                arrs[i,j]=Convert.ToInt32( putin2[j]);
            }
        }
        Console.WriteLine(GetMax(arrs));
    }
    
    static int GetMax(int[,] arrs){
        if(arrs==null||arrs.Length==0)return 1;
        
        int[] s=null;
        int max=0;
        for(int i=0;i<arrs.GetLength(0);i++){  //i行
            s=new int[arrs.GetLength(1)];
            //指定行
            
            for(int j=i;j<arrs.GetLength(0);j++){  //结束行j,i-j行是我们讨论的范围
                int cur=0;
                for(int k=0;k<s.Length;k++){   //当前行 的 每一列
                    s[k]+=arrs[j,k];
                    cur+=s[k];
                    max=Math.Max(max, cur);
                    cur=cur<0?0:cur;
                }
            }
        }
        return max;
    }
}

3.空间压缩

在这里插入图片描述
分析:
1.我们首先分析,我们需要左边和上边两个位置的值,才可以确定当前位置的值,那么,我们就先把第一行和第一列的值,存起来

在这里插入图片描述

3.1 最小路径和 -空间压缩

牛客网链接
在这里插入图片描述
在这里插入图片描述
分析:
这里是我让我们求最小路径和,是在一个矩阵里面从00走到最右下角的位置。
而且每次只能往右和往下走,这就符合我们的空间压缩技巧
1.分析每个格子的情况
2.第一行,只能从左往右走,则从左往右累加即可推出第一行的数据
3.第一列,从上往下,则从上往下累加即可推出第一列的数据
4.其他格子,由于我们只能往右和下移动,那么格子只能从上和左来,所以,我们取左和上中较小的作为我们来的位置,然后累加
5.经过处理之后,arrs[i,j]位置的意义就是到达这个位置需要的时间,我们输出最右下的格子的时间,就是我们要求的
在这里插入图片描述
在这里插入图片描述

using System;
class Test{
    static void Main(){
        string[] putin=Console.ReadLine().Split(' ');
        int row=Convert.ToInt32(putin[0]);
        int line=Convert.ToInt32(putin[1]);
        int[,] arrs=new int[row,line];
        
        for(int i=0;i<row;i++){
            string[] getin=Console.ReadLine().Split(' ');
            for(int j=0;j<line;j++){
                arrs[i,j]=Convert.ToInt32(getin[j]);
            }
        }
        
        //对第一行进行初始化
        for(int j=1;j<line;j++){
            arrs[0,j]+=arrs[0,j-1];
        }
        
        //对第一列进行初始化
        for(int i=1;i<row;i++){
            arrs[i,0]+=arrs[i-1,0];
        }
        
        //对每个元素进行遍历
        for(int i=1;i<row;i++){
            for(int j=1;j<line;j++){
                arrs[i,j]+= Math.Min(arrs[i,j-1], arrs[i-1,j]);
            }
        }
        //经过处理之后,arrs[i,j]位置的意义就是到达这个位置需要的时间
        Console.WriteLine(arrs[row-1,line-1]);
    }
}

3.2 最长公共子序列 -空间压缩

牛客网链接
在这里插入图片描述
在这里插入图片描述
分析:
1.这是一个回文问题,我们找最长公共子序列应该是分析结尾。比如str1以i位置结尾,和sir2以i位置结尾的最长公共子序列是多少?

一个
2.我们知道了应该按字符串的结尾来分析,那么我们应该如何下手?我们可以通过str1和str2构造一个Matrix,Matrix[i,j]位置代表的就是当前位置的最长公共子序列的值
3.所以,第一行我们可以轻松的去判断,如果str1[0]中有一个和str2[i]位置匹配上了,那么i包括i后面的全为1 。第一列同理
4.对于Matrix[i,j]我们分析了四种情况如下

5.我们就可以推出当前的Matrix[i,j]位置的值,就是四种情况中最大的Max
6.我们得到了最长公共子序列的长度,但是我们如何得到子序列呢?我们的逻辑就是以字符串的结尾作为标准,即,以i结尾的最长公共子序列。那么我们应该从后往前推

在这里插入图片描述
7.推公共子序列的代码如下,其实就是找匹配的结尾字符
在这里插入图片描述

在这里插入图片描述

using System;
using System.Collections.Generic;
using System.Text;

class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * longest common subsequence
     * @param s1 string字符串 the string
     * @param s2 string字符串 the string
     * @return string字符串
     */
        public  string LCS(string s1, string s2)
    {
        // write code here
        if (s1.Length == 0 || s2.Length == 0) return "-1";

        int len1 = s1.Length;
        int len2 = s2.Length;

        int[,] Matrix = new int[len1, len2];

        //初始化第一行
        bool flag = false;
        for (int i = 0; i < len2; i++)
        {
            if (flag) Matrix[0, i] = 1;
            else if (s1[0] == s2[i])
            {
                Matrix[0, i] = 1;
                flag = true;
            }
            else
            {
                Matrix[0, i] = 0;
            }
        }
        flag = false;

        //初始化第一列
        for (int j = 0; j < len1; j++)
        {
            if (flag) Matrix[j, 0] = 1;
            else if (s2[0] == s1[j])
            {
                Matrix[j, 0] = 1;
                flag = true;
            }
            else
            {
                Matrix[j, 0] = 0;
            }
        }

		
        int MMax = 0;
        for (int i = 1; i < len1; i++)
        {
            for (int j = 1; j < len2; j++)
            {
                int p1 = Matrix[i - 1, j - 1];
                int p2 = Matrix[i, j - 1];
                int p3 = Matrix[i - 1, j];
                int p4 = Matrix[i - 1, j - 1] + (s1[i] == s2[j] ? 1 : 0);
                MMax =  Math.Max(Math.Max(p1, p2), Math.Max(p3, p4));
                Matrix[i, j] = MMax;
            }
        }

        int temp = MMax;
            if(temp==0)
            return "-1";
        int row = len1 - 1;
        int line = len2 - 1;
        StringBuilder ans = new StringBuilder();
        while (temp >= 0 )
        {
            if (row < 0 || line < 0) break;
            if (line == 0)
            {
                Console.Write(0);
            }
            //Console.WriteLine(Matrix[row, line]);
            if (s1[row] == s2[line])
            {
                //Console.WriteLine("Tatch: " + Matrix[row, line]);
                ans.Insert(0, s1[row]);
                temp--;
                row--;
                line--;
            }
            else if ((row >0 && line > 0)&& Matrix[row, line - 1] > Matrix[row - 1, line] )
            {
                line--;
            }
            else if ((row > 0 && line > 0) && Matrix[row - 1, line] > Matrix[row, line - 1])
            {
                row--;
            }
            else if (row == 0)
            {
                line--;
            }
            else if (line == 0)
            {
                row--;
            }
            else
            {
                line--;
                row--;
            }
        }
        return ans.ToString();


    }
}

3.3最长公共子串 -空间压缩

在这里插入图片描述
分析:最长公共字串我们就以串的最后一位进行判断,如果最后一位相等,则判断倒数第二位,一直往前
1213413999
6536313999
例如这里,从9一直往左判断到1,再往左就不是公共字串了

1.所以我们可以根据这个原理去构造我们的Matrix
2.首先第一行和第一列,只要相等就为1,不相等为0
3.其他的情况,Matrix[i,j],如果str1[i]与str[j]不相等,则为0,代表以这个位置结尾的公共字串不存在
如果相等,则为Matrix[i-1,j-1]+1,代表当前位置是字串的结尾

在这里插入图片描述

4.然后我们根据找到的最大值,一直往左上角加,加到0为止。因为0位置就是没有匹配,那么这个串就是我们要找的最长字串。

在这里插入图片描述

using System;

class test
{
    static void Main()
    {
        string s1 = Console.ReadLine();
        string s2 = Console.ReadLine();

        int len1 = s1.Length;
        int len2 = s2.Length;

        int[,] Matrix = new int[len1, len2];

        //第一行
        for (int j = 0; j < len2; j++)
        {
            if (s1[0] == s2[j]) Matrix[0, j] = 1;
        }
        //第一列
        for (int i = 0; i < len1; i++)
        {
            if (s2[0] == s1[i]) Matrix[i, 0] = 1;
        }
        int Max = 0;
        int Maxi=0;
        int Maxj=0;
        for (int i = 1; i<len1; i++)
        {
            for (int j = len2 - 1; j > 0; j--)
            {
                if (s1[i] == s2[j])
                {
                    Matrix[i, j] = Matrix[i - 1, j - 1] + 1;
                    if(Matrix[i, j]>Max){
                        Max=Matrix[i, j];
                        Maxi=i;
                        Maxj=j;
                    }
                }
                else
                {
                    Matrix[i, j] = 0;
                }

            }
        }
        //如果一个都没有,那就直接返回
        if(Max<=0){
            Console.WriteLine("-1");
            return;
        }
        
        //一直往左上角找,直到为0
        string ans = "";
        while (Maxi >= 0 && Maxj >= 0 && Matrix[Maxi, Maxj] != 0)
        {
            ans=ans.Insert(0, s1[Maxi].ToString());
            Maxi--;
            Maxj--;
        }

        Console.WriteLine(ans);
    }
}

4.输出最长递增子序列 -动态规划

在这里插入图片描述
在这里插入图片描述
分析:
这里我们有两种解题思路
第一种:动态规划
第二种:动态规划升级版-加个辅助数组
在这里插入图片描述
有了思路之后,我们试着去将这个过程实现
在这里插入图片描述

在这里插入图片描述

using System;
using System.Collections.Generic;
using System.Text;

class test
{
    static void Main()
    {
        int len = Convert.ToInt32(Console.ReadLine());
        //int len56 = Convert.ToInt32(Console.ReadLine());
        //int len = 459;
        string[] putin = Console.ReadLine().Split(' ');
        int[] arr = new int[len];

        for (int i = 0; i < len; i++)
        {
            arr[i] = Convert.ToInt32(putin[i]);
        }

        Console.WriteLine(maxIncSub(arr));

    }
    static string maxIncSub(int[] arr)
    {
        if (arr == null || arr.Length == 0)
        {
            return null;
        }
        else if (arr.Length == 1)
        {
            return arr[0].ToString();
        }
        int[] end = new int[arr.Length];
        int[] dp = new int[arr.Length];

        dp[0] = 1;
        end[0] = arr[0];
        int endright = 0; //end有效区的最后一位
        int startindex ;
        int endindex ;
        for (int i = 1; i < arr.Length; i++)
        {
            //二分查找 时间复杂度O(LogN)
            startindex = 0;
            endindex = endright;
            
            //找到小于arr[i]的数中最小的那个数
            while (startindex <= endindex)
            {
                int mid = (startindex + endindex) / 2;
                if (end[mid] > arr[i])//如果当前值大于目标值,则在当前值的左边找
                {
                    endindex = mid - 1;
                }
                else//如果当前值小于目标值,则在当前值的右边找
                {
                    startindex = mid + 1;
                }
            }

            //++endindex就是我们要找的位置
            end[++endindex] = arr[i];
            endright = Math.Max(endright, endindex);
            dp[i] = endindex+1;

            
            //for循环 时间复杂度O(N)会超时
            //for (int j = 0; j <= endright; j++)
            //{
            //    if (arr[i] < end[j])
            //    {
            //        //比当前位置小,则更新当前位置
            //        end[j] = arr[i];
            //        //更新dp
            //        dp[i] = endright + 1;
            //        isupdate = true;
            //    }
            //}
            //if (!isupdate)
            //{
            //    //如果没有更新则插入
            //    end[++endright] = arr[i];
            //    dp[i] = endright + 1;
            //}
        }

        int maxdp = 0;
        int maxdpindex = 0;
        for (int i = 0; i < dp.Length; i++)
        {
            if (dp[i] >= maxdp)
            {
                maxdp = dp[i];
                maxdpindex = i;
            }
        }
        
        StringBuilder ans = new StringBuilder();
        for (int i = maxdpindex; i >= 0; i--)
        {
            if (dp[i] == maxdp)
            {
                ans.Insert(0, arr[i].ToString()+" ");
                maxdp--;
            }
        }

        return ans.ToString();

    }
}

5.信封嵌套问题 -动态规划

牛客网链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
分析:这道题是前面 输出最长递增子序列 的一个扩展题,主要要做分析的就是怎么样将其提前排好序,然后再去进行动态规划

在这里插入图片描述
在这里插入图片描述

using System;
using System.Collections.Generic;
class test
{
    public class Node:IComparable<Node>
    {
        public int len;
        public int wid;
        public Node(int len, int wid)
        {
            this.len = len;
            this.wid = wid;
        }

        public int CompareTo(Node other)
        {
            if (this.len > other.len)
            {
                return 1;
            }
            else if (this.len == other.len)
            {
                if (this.wid > other.wid)
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }
            else
            {
                return -1;
            }
        }
    }

    static void Main()
    {
        
        int lenth = Convert.ToInt32(Console.ReadLine());
        List<Node> lists = new List<Node>();
        for (int i = 0; i < lenth; i++)
        {
            string[] putin = Console.ReadLine().Split(' ');
            Node node = new Node(Convert.ToInt32(putin[0]), Convert.ToInt32(putin[1]));
            lists.Add(node);
        }
        lists.Sort();
        
        int[] arr = new int[lists.Count];
        for(int i = 0; i < lists.Count; i++)
        {
            arr[i] = lists[i].wid;
        }
        //现在list已经排序好了
        int[] dp = new int[lists.Count];
        int[] end = new int[lists.Count];

        dp[0] = 1;
        end[0] = arr[0];
        int endright = 0;
        int startindex;
        int endindex;

        for (int i = 1; i < lists.Count; i++)
        {
            startindex = 0;
            endindex = endright;
            //二分查找找到
            while (startindex <= endindex)
            {
                int mid = (startindex + endindex) / 2;
                if (arr[i]>end[mid])
                {
                    startindex = mid + 1;
                }
                else
                {
                    endindex = mid - 1;
                }
            }
            //++endindex为我们要找的位置
            end[++endindex] = arr[i];
            endright = Math.Max(endright, endindex);
            dp[i] = endindex + 1;
        }

        //现在dp填完了,我们拿到最大的dp值返回
        int Max = 0;
        for (int i = 0; i < lists.Count; i++)
        {
            if (dp[i] > Max) Max = dp[i];
        }
        Console.WriteLine(Max);

    }
    
}

6.数字转换字符串 -动态规划

在这里插入图片描述
分析:
这是典型的尝试模型类题目,我们可以做的便是去尝试所有的可能,然后总结出规律。
也就是
从左往右试->暴力递归->动态规划
在这里插入图片描述
在这里插入图片描述

using System;
class test
{
    static void Main()
    {
        int n = Convert.ToInt32(Console.ReadLine());
        Console.WriteLine(GetNum(n));
    }

    static int GetNum(int n)
    {
        if (n < 1)
        {
            return 0;
        }
        char[] chars = n.ToString().ToCharArray();
        int length = chars.Length;
        int[] dp = new int[length + 1];
        dp[length] = 1;  //f(n)的值
        dp[length - 1] = chars[length - 1] == '0' ? 0 : 1;
        for (int i = length - 2; i >= 0; i--)
        {
            if (chars[i] == '0')
            {
                dp[i] = 0;
            }
            else
            {
                dp[i] = dp[i + 1];
                if(((chars[i] - '0') * 10 + (chars[i + 1] - '0')) < 27)
                {
                    dp[i] +=dp[i + 2];
                }
            }
        }

        return dp[0];
    }
}

7.牛牛背包问题 -动态规划

在这里插入图片描述
方法1-暴力递归
在这里插入图片描述

很神奇,在Java里面暴力递归可以通过
在这里插入图片描述

using System;
class test{
    static void Main(){
        string[] putin=Console.ReadLine().Split(' ');
        int count=Convert.ToInt32(putin[0]);
        int rest=Convert.ToInt32(putin[1]);
        
        string[] putarr=Console.ReadLine().Split(' ');
        int[] arr=new int[count];
        for(int i=0;i<count;i++){
            arr[i]=Convert.ToInt32(putarr[i]);
        }
        
        Console.WriteLine(GetWays(arr, 0, rest));
    }
    
    static int GetWays(int[] arr,int index,int rest){
        if(rest<0){
            //没有容量了
            return -1;
        }
        if(index==arr.Length){
            //越界了,无零食可选
            return 1;
        }
        
        int res1=GetWays(arr, index+1, rest);
        int res2=GetWays(arr, index+1, rest-arr[index]);
        return res1+(res2==-1?0:res2);
    }
}

方法二:暴动递归改动态规划
在这里插入图片描述

方法三:动态规划
在这里插入图片描述

using System;
class test
{
    static void Main()
    {
        string[] putin = Console.ReadLine().Split(' ');
        int count = Convert.ToInt32(putin[0]);
        int rest = Convert.ToInt32(putin[1]);

        string[] putarr = Console.ReadLine().Split(' ');
        int[] arr = new int[count];
        for (int i = 0; i < count; i++)
        {
            arr[i] = Convert.ToInt32(putarr[i]);
        }
        Console.WriteLine(int.MaxValue);
        Console.WriteLine(rest);
        Console.WriteLine(int.MaxValue> rest);
        
        int[,] dp = new int[count, rest+1];

        //初始化第一列
        for (int i = 0; i < count; i++)
        {
            dp[i, 0] = 1;
        }
        //初始化第一行
        for (int i = 0; i < rest+1; i++)
        {
            if (arr[0] == i) dp[0, i] = 1;
        }

        for (int i = 1; i < count; i++)
        {
            for (int j = 1; j < rest+1; j++)
            {
                dp[i, j] = (j - arr[i] >= 0 ? dp[i - 1, j - arr[i]] : 0) + dp[i - 1, j];
            }
        }


        //累加最后一行
        //因为最后一行代表的是选择count的情况下,可以装的全部情况
        int res = 0;
        for (int i = 0; i < rest+1; i++)
        {
            res += dp[count-1,i ];
        }

        Console.WriteLine(res);

    }


}

8.最小编辑代价 -动态规划

牛客网链接
https://www.nowcoder.com/study/live/718/3/21
分析:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


using System;
class test
{
    static void Main()
    {
        char[] str1 = Console.ReadLine().ToCharArray();
        char[] str2 = Console.ReadLine().ToCharArray();
        string[] putin = Console.ReadLine().Split(' ');
        int insert = Convert.ToInt32(putin[0]);
        int delete = Convert.ToInt32(putin[1]);
        int replace = Convert.ToInt32(putin[2]);
        
        int row=str1.Length+1;
        int line=str2.Length+1;
        //动态规划
        int[,] dp = new int[row, line];


        //初始化第一行
        for (int i = 1; i < line; i++)
        {
            dp[0, i] = dp[0, i - 1] + insert;
        }

        //初始化第一列
        for (int i = 1; i < row; i++)  
        {
            dp[i, 0] = dp[i - 1, 0] + delete;
        }

        for (int i = 1; i < row; i++)
        {
            for (int j = 1; j < line; j++)
            {

                //左上角
                int temp1 = (str1[i-1] == str2[j-1] ? 0 : replace) + dp[i - 1, j - 1];
                //左
                int temp2 = dp[i, j - 1] + insert;
                //上
                int temp3 = dp[i - 1, j] + delete;

                dp[i, j] = Math.Min(temp1, Math.Min(temp2, temp3));
            }
        }

        Console.WriteLine(dp[row-1, line-1]);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值