P1020 [NOIP1999 普及组] 导弹拦截
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
一行,若干个整数,中间由空格隔开。
输出格式
两行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
#include <bits/stdc++.h>
#define MAXN 100005
using namespace std;
int a[MAXN],res1[MAXN],res2[MAXN];
int now,cnt1,cnt2;
int main()
{
int tmp;
char c;
while(cin>>tmp) a[++now]=tmp; //这样输入才不会TLE
res1[0]=500010;
for(int i=1;i<=now;i++) //这里找最长不上升子序列
{
if(a[i]<=res1[cnt1]) res1[++cnt1]=a[i];
else //采用二分的方法
{
int l=1,r=cnt1;
while(l<r)
{
int mid=(l+r)>>1;
if(res1[mid]<a[i]) r=mid;
else l=mid+1;
}
res1[l]=a[i]; //这里写l写r都一样
}
}
cout<<cnt1<<endl;
res2[0]=-500010; //这里找最长上升子序列(即需要几套装置)
for(int i=1;i<=now;i++)
{
if(a[i]>res2[cnt2]) res2[++cnt2]=a[i];
else //采用二分的方法
{
int l=1,r=cnt2;
while(l<r)
{
int mid=(l+r)>>1;
if(res2[mid]>=a[i]) r=mid; //因为求的是上升子序列,所以有等于的情况也要替换
else l=mid+1;
}
res2[l]=a[i]; //这里写l写r都一样
}
}
cout<<cnt2;
}
看到题目要求是nlogn的时间复杂度,感觉应该要用二分法实现,但是不知道要用什么方法写来。看了题解的思路,在写第一问的时候,如果遇到一个数大于已有数组的最后一项,那就用二分往前查找(用二分的前提是已有的数组是单调不减的)找到比他大的第一个数,把它后面的数换成新加入的这个数。
第二问通过题解我了解了一个新的定理——Dilworth 定理。
把序列分成不上升子序列的最少个数,等于序列的最长上升子序列长度。
把序列分成不降子序列的最少个数,等于序列的最长下降子序列长度。
则第二问等价于最长上升子序列。
这题还要注意的是二分的时候,上升子序列和不上升子序列,在划定左右区间的时候,等号归属哪边的问题。
P8707 [蓝桥杯 2020 省 AB1] 走方格
题目描述
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第 n 行,从左到右依次为第 1 至第 m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 行第 1 列,要走到第 n 行第 m 列。只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
输入格式
输入一行包含两个整数 n,m。
输出格式
输出一个整数,表示答案。
#include <bits/stdc++.h>
using namespace std;
int n,m;
int walk(int x,int y)
{
int ans=0;
if(x==1||y==1) return 1;
else if(x%2==0&&y%2==0) return 0;
else
{
ans+=walk(x-1,y);
ans+=walk(x,y-1);
}
return ans;
}
int main()
{
cin>>n>>m;
int res=walk(n,m);
cout<<res;
return 0;
}
从(1,1)到(n,m)的dfs做法只有80分,最后一个测试点TLE了。改成了从(n,m)到 (1,1)过了
P1115 最大子段和
题目描述
给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个整数,表示序列的长度 n。
第二行有 n 个整数,第 i 个整数表示序列的第 i 个数字 ai。
输出格式
输出一行一个整数表示答案。
#include <bits/stdc++.h>
using namespace std;
int f[200005];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>f[i];
for(int i=2;i<=n;i++)
f[i]=max(f[i],f[i-1]+f[i]);
sort(f+1,f+1+n);
cout<<f[n];
}
数组一开始开小了,WA了三个数据
B3637 最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由 n(n≤5000) 个不超过 10^6 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 n,表示序列长度。
第二行有 n 个整数,表示这个序列。
输出格式
一个整数表示答案。
#include <bits/stdc++.h>
using namespace std;
int f[5005];
int dp[5005];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>f[i];
dp[i]=1;
}
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++)
{
if(f[i]>f[j])
dp[i]=max(dp[i],dp[j]+1);
}
sort(dp+1,dp+1+n);
cout<<dp[n];
}
P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles
题目描述
观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。
在上面的样例中,从 7→3→8→7→5 的路径产生了最大权值。
输入格式
第一个行一个正整数 r ,表示行的数目。
后面每行为这个数字金字塔特定行包含的整数。
输出格式
单独的一行,包含那个可能得到的最大的和。
#include <bits/stdc++.h>
using namespace std;
int f[1005][1005];
int dp[1005][1005];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=i;j++)
cin>>f[i][j];
dp[1][1]=f[1][1];
for(int i=2;i<=n;i++)
for(int j=1;j<=i;j++)
dp[i][j]=max(dp[i-1][j-1]+f[i][j],dp[i-1][j]+f[i][j]);
int maxx=0;
for(int k=1;k<=n;k++)
{
if(dp[n][k]>maxx) maxx=dp[n][k];
}
cout<<maxx;
}