1020 导弹拦截
200分的普及组真题,分三道,需要全部训练,训练动态规划,动态规划和搜索以及排序都是非常重要的
先从dp,再从fs,最后排序
首先这个题需要求两个值,一个最长不下降子序列,因为我们要拦截这个导弹,一个是最长下降子序列,因为我们要让拦截的系统变小
好了,我们发现一个东西,也就是f数组就是相当于方程中的x,所以f数组的定义就是以i结尾的最长不上升子序列的长度,然后我们需要用一个d数组来辅助f变量,d数组维护的是导弹的最后一个位置,然后再用t记录最大的答案就好了
其实感觉并不难,很经典的
今天wyq教练给我们出了一道题,叫做分割一段数列使得每一段数列要么递增要么递减,一个f数组不够用,需要用递增递减数组d和u来维护
--------------------过去--------------------------------
这个问题有两问,很明显是求一个最长下降子序列的长度问题,可以用很简单的DP来算,也可以用高效率,但这个题规模不太需要
设a[x]表示原序列第x个元素,b[x]表示长度为x的下降子序列的长度
处理到a[x]的时候,可以查找它连接到长度最大是多少的下降子序列,假设最大长度是maxx,那么b[x]=maxx+1,b数组被赋值的最大值就是第一问的答案
第二问,求的是最长上升子序列,或者使用 贪心的方法,也就是每一个导弹来袭的时候,使用这颗导弹的防御系统中上一次拦截导弹高度最低的那一套来拦截,如果不存在,就用新的系统
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#define N 1001
using namespace std;
int a[N],b[N],c[N];
int main()
{
int n=0,maxx=1;
while(scanf("%d",&a[n++])!=EOF)//循环输入
for(int i=0;i<n;i++)
{
b[i]=1;
for(int j=0;j<i;j++)
if(a[j]>=a[i]&&b[j]+1>b[i]) b[i]=b[j]+1;
maxx=max(maxx,b[i]);
}
printf("%d\n",maxx);
maxx=1;
for(int i=0;i<n;i++)
{
c[i]=1;
for(int j=0;j<i;j++)
if(a[j]<a[i]&&c[j]+1>c[i]) c[i]=c[j]+1;
maxx=max(maxx,c[i]);
}
printf("%d\n",maxx);
return 0;
}
-----------------------重新-------------------------------------
200分的题目,一个是最长下降子序列的长度
关于这样的线性dp很常见,就是求一个连续的序列,不能下降,意思就是可以相等或者大于
while(scanf(“%d”,&a[n++])!=EOF)//循环输入
意思就是输入以后存入了a数组里面
在子序列中,如果我们枚举到i,再来一个j从0开始到达i,那么,我们需要用逆向思维,意思就是说,正常序列是不下降,也就是前面的要大于等于后面的,那么,我们枚举的时候就要翻过来
一般动态规划都是这样的 (经验值加1)
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
int n=0,a[100001],f[100001],d[100001],ans=1,t=0;
int main()
{
while(~scanf("%d",&a[++n]));
n--;
for(int i=1; i<=n; i++)
{
f[i]=1;//标记?
//应该是长度
for(int j=t; j>0; j--)
if(a[i]<=a[d[j]]) //满足序列
//不下降子序列,后面的必须不能小
{
f[i]=f[d[j]]+1;//长度加一
//添加元素
break;
}
t=max(t,f[i]);//获取最长子序列长度
d[f[i]]=i;//记录编号
ans=max(ans,f[i]);//获取最长子序列
}
printf("%d\n",ans);
ans=1;
t=0;
for(int i=1; i<=n; i++)
{
f[i]=1;
for(int j=t; j>0; j--)
if(a[i]>a[d[j]])//下降子序列
{
f[i]=f[d[j]]+1;
break;
}
t=max(t,f[i]);
d[f[i]]=i;
ans=max(ans,f[i]);//同样的处理方法
}
printf("%d",ans);
}