dp的一种很简单的题型。
小编便以两道题来介绍一下。
问题一:防卫导弹
题目描述
一种新型的防卫导弹可截击多个攻击导弹。它可以向前或向下飞行,但不可以向后或向上飞行。它有一个缺点,尽管它发射时可以达到任意高度,但它只能截击比它上次截击导弹时所处高度低或者高度相同的导弹。现对这种新型防卫导弹进行测试,在每一次测试中,发射一系列的测试导弹,该防卫导弹所能获得的信息包括各进攻导弹的高度,以及它们的发射次序。求在每次测试中,该防卫导弹最多能截击的进攻导弹数量。
输入
第1行有若干个整数hi(0≤hi≤32767),表示进攻导弹的高度,其中导弹数不超过4000个。
输出
一个整数,表示最多能截击的进攻导弹数。
样例输入
36 25 45 17 22 28
样例输出
3
分析:
选择一个导弹,之后只能选比它高度低的导弹。中间比开始的导弹高的就会跳过或去除(不计入)。这种 在一段序列中去除一些、而得出最长的一段单调序列的问题,即是dp经典题型:最长单调子序列问题。
最长非降子序列的长度。 (讲DP基本都会讲到的一个问题LIS:longest increasing subsequence)
代码
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
int n,a[4001],f[4001];
int main(){
while(scanf("%d",&a[++n])!=EOF)
;
n--;
// 检验输入
// for(int i=1;i<=n;i++) cout<<a[i]<<' ';
//核心代码
for(int i=1;i<=n;i++){
f[i]=1;
for(int j=1;j<i;j++)
if(a[j]>=a[i]) f[i]=max(f[i],f[j]+1);
}
int res=0;
for(int i=1;i<=n;i++) res=max(res,f[i]);
cout<<res<<endl;
}
1、不定输入之前讲过了,略。
2、最长单调子序列问题的核心代码如下:
for(int i=1;i<=n;i++){
f[i]=1;
for(int j=1;j<i;j++)
if(a[j]>=a[i]) f[i]=max(f[i],f[j]+1);
}
dp[i]即f[i]表示以i为结尾的最长单调(这里是递减)子序列。
所以以这个定义方法,要找出最长的,需要遍历dp[i];
int res=0;
for(int i=1;i<=n;i++) res=max(res,f[i]);
问题二:合唱队形
题目描述
n位同学站成一排,音乐老师要请其中的(n-K)位同学出列,使得剩下的K位同学排成合唱队形。合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2,…,K,他们的身高分别为T1,T2,…,TK,则他们的身高满足T1<…Ti+l>…>TK(1≤i≤K)。你的任务是:已知所有n位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入
第1行是一个整数n(2≤n≤100),表示同学的总数。
第2行有n个整数,用空格分隔,第i个整数Ti (130≤Ti≤230)是第i位同学的身高(厘米)。
输出
一个整数,就是最少需要几位同学出列。
样例输入
8
186 186 150 200 160 130 197 220
样例输出
4
题目分析:
dp,最长递增或递减子序列的变形,找到以i结尾的左侧最长递增子序列,和以i开头的右侧最长子序列,求和值最大的情况.
仔细体会dp问题中利用递推,以及状态的保存。
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
int n,a[101],dp[2][101],sum;
int main(){
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
//找出以i结尾的最长递增子序列
for(int i=0;i<n;i++){
dp[0][i]=1;
for(int j=0;j<i;j++){
if(a[i]>a[j]) dp[0][i]=max(dp[0][i],dp[0][j]+1);
}
}
//找出以i开头的最长递减子序列(逆序的递增)
for(int i=n-1;i>=0;i--){
dp[1][i]=1;
for(int j=n-1;j>i;j--){
if(a[i]>a[j]) dp[1][i]=max(dp[1][i],dp[1][j]+1);
}
}
for(int i=0;i<n;i++) sum=max(sum,dp[0][i]+dp[1][i]);
//i在dp1和dp2中算了两遍,最后要减去
cout<<(n-sum+1)<<endl;
}