文章目录
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.最小编辑代价 -动态规划
牛客网链接
分析:
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]);
}
}