第一题:
这是一个简单的动规板子题。
给出一个由 n(n≤5000) 个不超过 10^6的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 n,表示序列长度。
第二行有 n 个整数,表示这个序列。
输出格式
一个整数表示答案。
代码:#include<bits/stdc++.h>
using namespace std;
int n;
struct c{
int a,b;
}a[5005];
bool cmp(c x,c y){
return x.b<y.b;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i].a;
a[i].b=1;
}
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++){
if(a[j].a<a[i].a) a[i].b=max(a[i].b,a[j].b+1);
else continue;
}
}
sort(a+1,a+1+n,cmp);
cout<<a[n].b;
return 0;
}
解题思路:对于输入的数据,从第二个数开始,和他前面的数比较大小,如果大于,则判断前一个数的最长上升子序列加一的长度和这个数自身最长上升子序列的长度,赋值为数值较大的那一个。最后在对所有数据的最长上升子序列排序。
第二题:
题目描述
给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。
输入格式
第一行是一个整数,表示序列的长度 n。
第二行有 n 个整数,第 i个整数表示序列的第 i 个数字 a_i。
输出格式
输出一行一个整数表示答案。
代码:#include<bits/stdc++.h>
using namespace std;
int n,a,b,ans=-1e10;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a;
if(i==1) b=a;
else b=max(a+b,a);
ans=max(ans,b);
}
cout<<ans;
return 0;
}
解题思路:把每一次输入的数赋值给a,将第一个输入的数作为起始b,然后判断a和a+b的大小,取较大,并和ans作比较,大于则赋值给ans,最后输出ans。
第三题:
题目描述
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 至第 n 行,从左到右依次为第 1 至第 m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 行第 1 列,要走到第 n 行第 m 列。只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
输入格式
输入一行包含两个整数 n,m。
输出格式
输出一个整数,表示答案。
代码:#include<bits/stdc++.h>
using namespace std;
int n,m,a[35][35];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) a[i][1]=1;
for(int i=1;i<=m;i++) a[1][i]=1;
for(int i=2;i<=n;i++){
for(int j=2;j<=m;j++){
if(i%2==0&&j%2==0) a[i][j]=0;
else a[i][j]=a[i-1][j]+a[i][j-1];
}
}
cout<<a[n][m];
return 0;
}
解题思路:构建一个n*m的矩形,矩形边框上的标记为1,然后遍历剩余的点,如果坐标均为偶数,则赋值为零,如果不是,则赋值为上方和左方两个点的数值之和。
第四题:
观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。
在上面的样例中,从 7→3→8→7→5 的路径产生了最大权值。
输入格式
第一个行一个正整数 r ,表示行的数目。
后面每行为这个数字金字塔特定行包含的整数。
输出格式
单独的一行,包含那个可能得到的最大的和。
代码:#include<bits/stdc++.h>
using namespace std;
int n,a[1005][1005];
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
cin>>a[i][j];
}
}
for(int i=n-1;i>=1;i--){
for(int j=1;j<=i;j++){
a[i][j]+=max(a[i+1][j],a[i+1][j+1]);
}
}
cout<<a[1][1];
return 0;
}
解题思路:本题从下往上计算,从倒数第二行开始,计算这一行和对应最后一行的两个数的和的最大值,然后依次向上计算,最后第一行第一个数为答案。
第五题:
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入格式
一行,若干个整数,中间由空格隔开。
输出格式
两行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
代码:
#include<bits/stdc++.h>
using namespace std;
int x,counta,a[100005];
int countb,b[100005];
int main(){
while(scanf("%d",&x)!=-1){
if(counta==0||a[counta]>=x) a[++counta]=x;
else{
int l=1,r=counta;
while(l<r){
int mid=(l+r)/2;
if(a[mid]<x)r=mid;
else l=mid+1;
}
a[l]=x;
}
if(countb==0||b[countb]<x) b[++countb]=x;
else{
int l=1,r=countb;
while(l<r){
int mid=(l+r)/2;
if(b[mid]>=x) r=mid;
else l=mid+1;
}
b[l]=x;
}
}
printf("%d\n%d\n",counta,countb);
return 0;
}
解题思路:根据狄尔沃斯定理,第一问所求的最长不上升子序列的长度可以转换为求下降子序列的个数;第二问等于求最长上升子序列长度,等同于求最长不上升子序列个数。
第一问:把输入的第一个数加给a数组,然后对后面的每一个数都进行判断,如果小于目前数组储存的最小的,则新加入一个数,如果不是,则用二分法判断用这个数替换当前数组中的哪一个。
第二问:类似于第一问,如果大于目前数组储存的最大的,则替换;如果不是,则二分查找后替换。