week10作业

B - LIS & LCS

东东有两个序列A和B。
他想要知道序列A的LIS和序列AB的LCS的长度。
注意,LIS为严格递增的,即a1<a2<…<ak(ai<=1,000,000,000)。Input第一行两个数n,m(1<=n<=5,000,1<=m<=5,000)
第二行n个数,表示序列A
第三行m个数,表示序列BOutput输出一行数据ans1和ans2,分别代表序列A的LIS和序列AB的LCS的长度Simple Input
5 5
1 3 2 5 4
2 4 3 1 5
Simple Output
3 2

解题思路:

    对于LIS与LCS属于比较经典的问题,做动态规划的题目,自己感觉关键一步在于定状态,状态定的好坏直接决定了解的正确性与复杂度高低,动态规划的题目要求思维逻辑比较复杂,大脑难以想象清楚,我们实际属于一种甩锅的做法去解决问题。而定状态属于将动态的规划为静态的一个过程。我们大脑需要的正是一种静的便于思考的一种问题模式。
    对于LIS问题而言,其大致上属于一种暴力的解法,但其中也有着定状态的体现,比如fun[i]表示的是以i个元素结尾的最长上升子序列的大小,这便是一种静态的体现。定状态的一个最终目标是巧妙利用之前求出过得变量,比如对于本题而言,LIS的解法中我们保留的是以第i个元素结尾的最长上升子序列,我们并没有保留前i个元素中的最长上升子序列,对于后者而言,其保留的信息不容易得到有效利用,也就是说难以递归或递推下去,因为我们关注的是一种上升结构,而对于后一种做法我们并没有保留子序列上升到哪里的信息。

对于LCS问题而言,解决方案比较巧妙,用到了增维的做法,这种思想我们在前面几篇文章中也提到过,增维思想在我们处理一些存在特殊元素的时候经常使用,同样,在我们处理一些比较棘手的问题时,也可以进行该方向上的思考,因为维度越高,情况分的就越细,保留的数据就越多,数据应用起来也就越方便。另外,对于LCS而言,核心代码if(a[i]==b[j])
{
cc[i][j]=cc[i-1][j-1]+1;
}
else
{
cc[i][j]=max(cc[i-1][j],cc[i][j-1]);
}
比较简单,但其中思想是不简单的,if中的判断属于一种扩充,else中的语句属于一种继承,扩充的形式只有一种,也就是当两端的元素相等时,这也是保留二维的原因所在。其余就按照继承来处理。我们也可以对比LIS与LCS,关注而这的状态是怎么定的,进行一定的感悟理解。

#include<iostream>
#include<stdio.h>
#include<cmath>
#include<string.h> 
using namespace std;
long long a[5005],b[5005];
int n,m;
int fun[5005]={0};
int label;
int cc[5005][5005];
int func1()
{
	fun[1]=1;
	label=1;
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<i;j++)
		{
			if(a[j]<a[i])
			{
				fun[i]=max(fun[i],fun[j]+1);
			}
		}
		label=max(label,fun[i]);
	}
	return label;
}
int func2()
{
	memset(cc,0,sizeof(cc));
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(a[i]==b[j])
			{
				cc[i][j]=cc[i-1][j-1]+1;
			}
			else
			{
				cc[i][j]=max(cc[i-1][j],cc[i][j-1]); 
			}
		}
	}
	return cc[n][m];
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		fun[i]=1;
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%lld",&b[i]);
	}
	cout<<func1()<<" "<<func2()<<endl;
}在这里插入代码片

C - 拿数问题 II

YJQ 上完第10周的程序设计思维与实践后,想到一个绝妙的主意,他对拿数问题做了一点小修改,使得这道题变成了 拿数问题 II。
给一个序列,里边有 n 个数,每一步能拿走一个数,比如拿第 i 个数, Ai = x,得到相应的分数 x,但拿掉这个 Ai 后,x+1 和 x-1 (如果有 Aj = x+1 或 Aj = x-1 存在) 就会变得不可拿(但是有 Aj = x 的话可以继续拿这个 x)。求最大分数。
本题和课上讲的有些许不一样,但是核心是一样,需要你自己思考。Input
第一行包含一个整数 n (1 ≤ n ≤ 105),表示数字里的元素的个数
第二行包含n个整数a1, a2, …, an (1 ≤ ai ≤ 105)
Output
输出一个整数:n你能得到最大分值。
Example
Input
2
1 2
Output
2
Input
3
1 2 3
Output
4
Input
9
1 2 1 3 2 2 2 2 3
Output
10

解题思路:

整体来讲,这个题目还算比较简单,在这里面存在一个关系的概念,而题目中已经明确给出了,拿完一个数其数值左右的两个数则变得不能拿,而我们习惯性的从小到大去思考,所以反映在解法上便是此数与其前两个数有直接关联,而再之前的数值我们会利用“甩锅处理”,我们不再需要考虑。另一个点是注意数据的范围,这也属于思维逻辑的一部分,需要强化。

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
int n;
int fun[100005];
long long fun2[100005];
int func;
int main()
{
	memset(fun,0,sizeof(fun));
	memset(fun2,0,sizeof(fun2));
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&func);
		fun[func]++;
	}
	fun2[1]=fun[1];
	for(int i=2;i<100005;i++)
	{
		fun2[i]=max(fun2[i-1],fun2[i-2]+(long long)fun[i]*i);
	}
	cout<<fun2[100004]<<endl; 
}在这里插入代码片
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值