想看更多的解题报告: 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; }