LCS LIS 记忆化搜索 黑书 动态规划初学通俗版

文章介绍了如何使用动态规划方法解决两个字符串之间的最长公共子序列问题以及最长递增子序列问题。通过二维数组存储中间状态,避免重复计算,优化算法效率。同时,给出了不同情况下的状态转移方程和代码实现。
摘要由CSDN通过智能技术生成

最长公共子序列

在两个字符串中,找最长相等的不是子串,是删除个别字母的相等的串
A={a,b, c,d,f,g,a,b}

B={a, c ,b,f,g,a, d,c}

为5

分两种情况

  1. 遍历到这两个字符相等,就在两字符前一个基础上加1,更新状态
  2. 遍历到两个字符不相等,就在两字符的各前一个字符所记录的最长序列数里选最大的(前状态得后状态)
#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int dp[100][100];
int n,m;
int lcs(){
	//遍历不用管具体的,解决子问题就都解决了 
	for(int i=1;i<=s1.length();i++)
	for(int j=1;j<=s2.length();j++)
	if(s1[i-1]==s2[j-1])//前一个字符相等     
	dp[i][j]=dp[i-1][j-1]+1;//在后一个基础上+1   //状态更新 
	else//不等 
	dp[i][j]=max(dp[i-1][j],dp[i][j-1]);//选i相等字符和j相等字符中最大的 //状态更新 
	return dp[s1.length()][s2.length()];
}
int main()
{
  cin>>n>>m;
  cin>>s1>>s2;
  cout<<lcs();
  return 0;
}

滚动数组

i,j都会被遍历,选择一个进行覆盖就可以

#include<bits/stdc++.h>
using namespace std;
string s1,s2;
int d[100];
int lcs(){
	for(int i=1;i<=s1.length();i++)
	for(int j=1;j<=s2.length();j++)
	if(s1[i-1]==s2[j-1])
	d[j]=d[j-1]+1;//原来的i会被覆盖 
	else
	d[j]=max(d[j],d[j-1]);
    if(s2.length()<s1.length())
	return d[s2.length()];
    else
    return d[s1.length()];
}
int main()
{
  cin>>s1>>s2;
  cout<<lcs();
  return 0;
}

最长递增公共子序列

lis

hdu1257
Problem Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.
Input
输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)
Output
对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
Sample Input
8 389 207 155 300 299 170 158 65
Sample Output
2

最长递增子序列,选择每一个导弹作为最后一个记录状态比较前面比它小的,在最长得递增里选到比它小的

d[i]记录状态

d[j]是选比它小的时候最长的那个子序列长度

#include<bits/stdc++.h> 
using namespace std;
int n,h[100],d[100];//d代表状态,在i导弹下的最长子序列 
int ans=0,mx=0;
int lis()
{
	d[1]=1; //第一个子序列为1 
	for(int i=2;i<=n;i++)
	{
	mx=0; //每个导弹的长度都要从自己的i的0开始 
	for(int j=1;j<i;j++) //i前面的小于i的数 
	  if(d[j]>mx&&h[j]<h[i])//以前面的导弹递增子序列进行更新
	  //如果这颗导弹没有大于另一颗导弹的子序列长度,就不进入 
	  //保证i是在最长的子序列中加 
	 {
	 	mx=d[j];
	 	d[i]=mx+1;
	 	ans=max(ans,d[i]);//比较所有最长的 
	 }
}
	 return ans;
}
int main()
{
	while(cin>>n)
	{
		for(int i=1;i<=n;i++)
		cin>>h[i];
		cout<<lis();
	}
	return 0;
}

lcs

排序,在比较最长的子序列,最长的子序列就是最长递增子序列

记忆化搜索

Figure 1 shows a number triangle. Write a program that calculates the highest sum of numbers passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right.
Input
Your program is to read from standard input. The first line contains one integer N: the number of rows in the triangle. The following N lines describe the data of the triangle. The number of rows in the triangle is > 1 but <= 100. The numbers in the triangle, all integers, are between 0 and 99.
Output
Your program is to write to standard output. The highest sum is written as an integer.
Sample Input
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30

在这里插入图片描述
题意是给定n层的三角形树塔,顶部第一个数往下走,只能走斜下方的左边右边,问走完的最大数字和是多少
用dfs来走每个选项,用d数组记录状态,同时判断走没走过,剪枝,减少重复搜索

#include<bits/stdc++.h>
using namespace std;
int n,d[100][100],s[100][100];//记录数值,同时判断走没走过
int dfs(int i,int j)
{
	if(i==n)
	return s[n][j];
	if(d[i][j]>0)   //走过就不在走 
	return dp[i][j];    //回到走过时所取得的值 
	return dfs(i,j)=max(dfs(i+1,j),dfs(i+1,j+1))+s[i][j];//选择最大的路径走 
}

 

蓝桥省赛3 滑行

  • 题目

小蓝准备在一个空旷的场地里面滑行,这个场地的高度不一,小蓝用一个 n 行 m 列的矩阵来表示场地,矩阵中的数值表示场地的高度。

如果小蓝在某个位置,而他上、下、左、右中有一个位置的高度(严格)低于当前的高度,小蓝就可以滑过去,滑动距离为 1 。

如果小蓝在某个位置,而他上、下、左、右中所有位置的高度都大于等于当前的高度,小蓝的滑行就结束了。

小蓝不能滑出矩阵所表示的场地。

小蓝可以任意选择一个位置开始滑行,请问小蓝最多能滑行多远距离。

输入格式

输入第一行包含两个整数 n, m,用一个空格分隔。

接下来 n 行,每行包含 m 个整数,相邻整数之间用一个空格分隔,依次表示每个位置的高度。

输出格式

输出一行包含一个整数,表示答案。

样例输入

4 5
1 4 6 3 1 
11 8 7 3 1 
9 4 5 2 1 
1 3 2 2 1

样例输出

7

样例说明

滑行的位置一次为 (2,1),(2,2),(2,3),(3,3),(3,2),(4,2),(4,3)(2,1),(2,2),(2,3),(3,3),(3,2),(4,2),(4,3)。

  • 题解

dfs来走,vis来记录,选择好判断标准即要出发的要大于走出去的高度

因为走出很多步会有不同的数,用mx来记录选择最大的数,就是vis要走的

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[105][105];
int vis[105][105]={0};
int d[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
bool check(int i,int j){  //判断能不能走
	if(i<0||j<0||i>=n||j>=m)
	return false;
	return true;
}
int dfs(int i,int j){
   int 	mx=0;
  if(!check(i,j)) return 0;//走不了步数为0
  if(vis[i][j]) return vis[i][j];//判断走没走过,走过直接输出
  for(int x=0;x<4;x+=1)
   {
    if(a[i][j]>a[i+d[x][0]][j+d[x][1]])
    mx=max(mx,dfs(i+d[x][0],j+d[x][1]));//用一个mx记录上下左右走出来的最大值
   }   
   vis[i][j]=mx+1;//在上下左右走完的基础上算上迈出去的一步
   return vis[i][j];
}
int main(){
	cin>>n>>m;
	int ans=0;
	for(int i=0;i<n;i++)
	for(int j=0;j<m;j++)
	cin>>a[i][j];
	for(int i=0;i<n;i++)
	for(int j=0;j<m;j++)
     ans=max(ans,dfs(i,j));//比较选择最大的
	  cout<<ans<<endl;
	return 0;
}

递推和动态规划记录状态

状态从最后一层向上更新利于选择判断

#include<bits/stdc++.h>
using namespace std;
int n,d[100][100],s[100][100];
int dpp()
{
	for(int j=n;j>0;j--)//最后一层倒推,为最后一层赋值 
	d[n][j]	=a[n][j];
	for(int i=n;i>0;i--)
	for(int j=1;j<i;j++)
	d[i][j]=max(d[i+1][j],d[i+1][j+1])+a[i][j]; //选择最大的走 
	return d[1][1];
 } 

 

不懂私信评论吼

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值