拦截导弹
问题 J: 拦截导弹
题目描述
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出的高度数据是≤50000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
输入
1行,若干个整数(≤100000),为导弹依次飞来的高度
输出
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。
样例输入
389 207 155 300 299 170 158 65
样例输出
6
2
分析
题目有两点要求:
- 算出最长下降子序列
- 算出下降子序列的个数
对于第一点,把数组倒过来,用lower_bound()
算出最长递增子系列,就可以了,对于第二点,有一个什么定理,相当于计算原来数列最长上升子序列的长度,计算方法一样
代码
int v[1000005];
int dp[1000005],f[1000005];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
//
// freopen("E:/Code/C++/untitled1/input.txt","r",stdin);
// freopen("output.txt","w",stdout);
mem(v);mem(dp);mem(f);//初始化
int t,cnt = 0;
while (cin >> t)
{
v[cnt++] = t;
}//cnt = size
//算第二点
f[1] = v[0];//边界,记录长度为1的子序列的最小末尾值
dp[0] = 1;//记录长度
int cnt_f = 1;//记录最大的子序列长度
for(int i = 0;i < cnt;++i)
{
auto h = lower_bound(f+1,f+cnt_f + 1,v[i])-(f+1);//使用二分查找找到v[i]比哪个长度的子序列末端大
f[h+1] = v[i];//更新子序列末端的值
if(h+1>cnt_f) cnt_f = h+1;//更新最大子序列长度
dp[i] = h+1;//de3(t,v[i],dp[i]);
}
int ans = cnt_f;
//算第一点
mem(f);mem(dp);
for(int i = 0;i < cnt/2;++i)
swap(v[i],v[cnt-i-1]);
cnt_f = 1;
dp[0] = 1;
f[1] = v[0];
for(int i = 1;i < cnt;++i)
{
auto h = lower_bound(f+1,f+cnt_f + 1,v[i]) - (f + 1);
f[h+1] = v[i];
if(h+1 > cnt_f) cnt_f = h+1;
dp[i] = h + 1;
}
cout << cnt_f << endl << ans;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战