想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
转载请注明出处:http://blog.csdn.net/wangjian8006
拦截导弹
某国为了防御敌国的导弹袭击,开发出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:
虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。
某天,雷达捕捉到敌国的导弹来袭,并观测到导弹依次飞来的高度,请计算这套系统最多能拦截多少导弹。
拦截来袭导弹时,必须按来袭导弹袭击的时间顺序,不允许先拦截后面的导弹,再拦截前面的导弹。
输入有两行,
第一行,输入雷达捕捉到的敌国导弹的数量k(k<=25),
第二行,输入k个正整数,表示k枚导弹的高度,按来袭导弹的袭击时间顺序给出,以空格分隔。
输出只有一行,包含一个整数,表示最多能拦截多少枚导弹。
我们拿300 207 155 300 299 170 158 65举例
用a[1..i]表示第i个导弹的高度,用f[1..i]表示第i个导弹最多能打f[i]个导弹,即第i个的最长降序子列。那么我们从最后到第一个开始分析,可以肯定的是,最后一个的最长降序子列肯定是1,即自己本身,那么第二个如果比第一个高,那么就是2,否则就是1.这个很容易理解,相对于第三个,如果比第一个高,那么是f[n-2] = f[n] + 1,如果比第二个高,那么f[n-2] = f[n-1] +1,那么值最大的就是f[n-2]的值,依据这样我们可以得到一个表达式,f[i]={max(f[k])+1(i<k<=n),1},当然前提条件是a[i]>a[k],那么根据这样推算,例子的f[i]的值是,6 4 2 4 3 2 1 0
代码如下:
#include<iostream>
using namespace std;
const int MAXN =100
int n, ans= 0;
int num[MAXN+ 1];
int f[MAXN+ 1];
void solve(){
inti, j;
f[1] = 1;
for (i = 2; i <= n; i++){
f[i] = 1;
for (j = 1; j <= i -1; j++)
if (num[j] >= num[i] && f[j] + 1 > f[i])
f[i] = f[j] + 1;
}
int ans;
for (i = 1; i <= n; i++)
if (f[i] > ans) ans= f[i];
cout<< ans<< endl;
}
int main(){
int i;
cin>> n;
for (i = 1; i <= n; i++)
cin>> num[i];
solve();
return 0;
}
合唱队队形:
题目描述:
N 位同学站成一排,音乐老师要请其中的(N‐K)位同学出列,使得剩下的K位同学不交换位置
就能排成合唱队形。
合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K,他们的身高分别为T1,T2,…,TK,则他们的身高满足T1<T2<…<Ti,Ti>Ti+1>…>TK(1<=i<=K)。
你的任务是,已知所有N 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
根据前面最长子序列,我的想法是,i从1到k遍历,以i为中心,算出从1到i的最长上升子序列a,i到k的最长下降子序列b,那么a+b-1就是以i为中心的剩下的最多的人数,那么从1到k选个最大的就可以了。
代码如下:
#include <iostream>
using namespace std;
#define MAXV 100
int n,a[MAXV];
int maxshangsheng(int t){
int i,j,temp[MAXV];
for(i=1;i<=t;i++){
temp[i] = 1;
for(j=1;j<=i;j++)
if(a[j]<a[i] && temp[j]>=temp[i])
temp[i] = temp[j] + 1;
}
return temp[t];
}
int maxxiajiang(int t){
int i,j,temp[MAXV];
for(i=n;i>=t;i--){
temp[i] = 1;
for(j=n;j>=i;j--)
if(a[j]<a[i] && temp[j]>=temp[i])
temp[i] = temp[j] + 1;
}
return temp[t];
}
int main(){
int i,s,dp;
scanf("%d",&s);
while(s--){
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
int res = -1;
for(i=1;i<=n;i++){
dp = maxshangsheng(i) + maxxiajiang(i) - 1;
if(dp>res) res = dp;
}
printf("%d\n",n - res);
}
return 0;
}
但是这样的缺点是,每次都要运算一边求最长的上升与下降子序列,会重复计算,并且消耗一定的时间,那么我们可以只算一遍,就是将i从1到n开始,将i的最长上升子序列保存在a[i]中,同样将i从n到1开始,将i的最长下降子序列保存在b[i]中,那么对于i的f[i],f[i]=a[i]+b[i]-1,这样就可以节省很多时间,虽然多出了两个数组,这也是用空间换时间的一个方法。
代码如下:
#include <iostream>
using namespace std;
#define MAXV 100
int n,a[MAXV],l[MAXV],r[MAXV];
void init(){
int i,j;
for(i=1;i<=n;i++){
l[i] = 1;
for(j=1;j<=i;j++)
if(a[j]<a[i] && l[j]>=l[i])
l[i] = l[j] + 1;
}
for(i=n;i>=1;i--){
r[i] = 1;
for(j=n;j>=i;j--)
if(a[j]<a[i] && r[j]>=r[i])
r[i] = r[j] + 1;
}
}
int main(){
int i,s;
scanf("%d",&s);
while(s--){
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
init();
int res = -1;
for(i=1;i<=n;i++){
if((l[i] + r[i] - 1)>res) res = (l[i] + r[i] - 1);
}
printf("%d\n",n - res);
}
return 0;
}