首页 技术 正文
技术 2022年11月12日
0 收藏 413 点赞 2,551 浏览 5230 个字

链接:

http://acm.hdu.edu.cn/showproblem.php?pid=1257

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28195

最少拦截系统

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)

Total Submission(s): 12863    Accepted Submission(s): 5100

Problem Description某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.

怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.
 Input输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)
 Output对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
 Sample Input

8 389 207 155 300 299 170 158 65

 Sample Output

2

 Source浙江工业大学第四届大学生程序设计竞赛 RecommendJGShining 

算法:贪心 || Dp 【本质一样】

思路:

算是很简单的一道题目了,但是从这里 学会了所谓的 LIS 优化,所以想好好总结下了
开始是用贪心做的了,复杂度 O(n^2)最容易理解的一种写法了。后来看了下这篇博客的分析:说是用“最长上升子序列的长度”http://www.cnblogs.com/dgsrz/articles/2384081.html复杂度 O(n^2)高了点,还是比较好理解的了。

/*
最长上升子序列(一维形式DP)
opt[i]=max(opt[j])+1, opt[i]) (0<=j<i 且num[j]<=num[i]) {最长非下降序列}
opt[i]=max(opt[j])+1, opt[i]) (0<=j<i 且num[j]>num[i]) {最长下降序列}
该算法复杂度为O(n^2)
*/

但是这样就还不如贪心了效率。后来又去找了下 LIS 的资料:http://hi.baidu.com/freezhan/item/c681309ef81ebbd0291647ed
http://www.cnblogs.com/okboy/p/3224186.html

发现 LIS 的 O(nlogn)的算法其实和贪心的本质很像,下面就以这一题仔细总结下。

样例中的数据是:
389 207 155 300 299 170 158 65

那么采取贪心的思路:

以数组 h[] 记录拦截系统当前的拦截高度,先初始化为最大值 INF = 30000+10,表示每一个新拦截系统都能拦截所有的导弹,然后遇到一个导弹就往前找看是否有已经使用了的系统能拦截,如果有,直接用;否则重新弄一个系统。最后再看用了几个系统就好了。
第一个导弹 389 < h[1] ( h[1] = INF)被第一个系统拦截 h[1] = 389第二个导弹 207 < h[1] 被第一个系统拦截 h[1] = 207第三个导弹 155 < h[1] h[1] = 155第四个导弹 300 > h[1] , 300 < h[2] ( h[2] = INF ) 所以新开发一个系统拦截第四个导弹, h[2] = 300第五个导弹 299 > h[1] , 299 < h[2] 被第二个系统拦截 h[2] = 299第六个导弹 170 > h[1] , 170 < h[2] h[2] = 170第七个导弹 158 > h[1] , 158 < h[2] h[2] = 158第八个导弹 65 < h[1] 被第一个系统拦截 h[1] = 65
所以最后使用了两个系统就拦截了所有的导弹【遍历 h[]数组从 1到 n 看有几个 != INF 就说明使用了】
导弹高度:389
207 155 300 299 170 158 65 使用的拦截系统: 1 1 1 2 2 2 2 1

下面说一下DP的 O(n^n)的思路:

dp[i] 记录的是拦截第 i 个导弹的系统的编号 index先初始化所有的导弹都被第一个系统拦截。dp[i] = 1
dp[i] = max(dp[i], dp[j]+1) 【0<=j<i && high[i] > high[j]】当遇到第 i 个导弹的时候,看前面已经拦截了的导弹的系统是否能够拦截第 i 个导弹 0 <= j < i一旦第 i 个导弹比第 j 个导弹要高,那么依照上面贪心的思路,拦截第 i 个导弹的系统必定比拦截第 j 个导弹的系统大,最少大一个【如果 (j < k < i) 如果第 k 个导弹没有比第 i 个低的】(不知道说清楚了没有,反正我是这么理解的了)那么最后出的 dp[]对应的值将是上面贪心的思想的 h[]的下标
导弹高度 a[]:389 207 155 300 299 170 158 65使用的拦截系统 dp[]: 1 1 1 2 2 2 2 1
最后输出最大的 dp[] 就是所需系统的个数了

最后总结下 Dp 的 O (nlogn)的思路:

其实本质上都一样了,就像这篇神奇的 LIS 的优化,奇怪的命名方式,看了我大半天才懂http://hi.baidu.com/freezhan/item/c681309ef81ebbd0291647ed

首先第一轮遍历所有的导弹,是不能改了的这里消耗 O(n)然后就是把第二重循环改成了二分而已 ,二分复杂度 O(logn)
首先我们要明确的是上面 Dp O(n^2) LIS 思想的第二轮找的是什么?
首先还是先定义一个 h[] 数组存储拦截系统的高度,那么根据前面贪心的分析:我们可以明确这一点只要是用过了的系统 h[] 那么它一定是单调递增的这个时候 h[] 保存的就是遍历到第 i 个导弹的时候,当前使用过的系统目前能够拦截导弹的最高值
所以第二轮找的是前面第一个 h[index] >= a[i] ,然后再更新 h[index]= a[i]永远维护 h[]是单调递增的,最后输出 h[]的使用长度,或是像上面贪心一样遍历一遍数出用了几个 h[] 都可以
那么再回到我们的关键问题:如何使得第二轮的顺序查找 O(n) 优化成 O(logn)注意到:h[]永远是单调递增的,那么直接写个二分查找就可以了。基本上对于这题可以 0 ms 秒过,最终发现这样也是网上传的 LIS 的最优的解法如果你想偷懒:那么二分也不用写了,直接加个 algorithm 的头文件了,调用 upper_bound就好了【kuangbin大神教的Orz】

int index = upper_bound(h,h+len+1,a[i])-h; //保证 h[index] 是数组 h 中第一个 >= a[i] 的

下面依次贴代码,看不懂的输出中间变量就好了。

code:

贪心:

D Accepted 236 KB 15 ms C++ 746 B

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;const int maxn = 1000+10;
const int INF = 30000+10; //导弹高度不会超过 30000
int a[maxn]; //存导弹的高度
int h[maxn]; // h[i] 表示第 i 个导弹系统拦截的最低高度int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
h[i] = INF; //初始化保证每一个拦截系统都能拦截所有的导弹
} for(int i = 0; i < n; i++)
{
for(int j = 0; j <= i; j++) //往前找开发了的导弹系统,看是否能拦截当前导弹, 最坏的结果是每个导弹都需要一个新的导弹系统来拦截,所以遍历到 i
{
if(h[j] >= a[i]) //一旦找到符合条件的拦截系统
{
h[j] = a[i]; // 第 j 个拦截系统拦截了第 i 个导弹 , 更新它的目前可以拦截的导弹的高度
break; //第 i 个导弹已经拦截,跳出里面那层循环
}
} } int tot = 0;
for(int i = 0; i < n; i++) //计算总共用了几个导弹系统
if(h[i] != INF) //如果第 i 个导弹系统的高度不等于初始值说明它用过
tot++;
printf("%d\n", tot);
}
return 0;
}

DP之 O(n^n)

D Accepted 236 KB 15 ms C++ 792 B
/*****************************************************
dp[i] = max(dp[i], dp[j]+1) 【0<=j<i, a[i] > a[j]】
如果当前导弹 i 的高度 > 前面的导弹 j 的高度,
那么拦截当前导弹 i 的系统,一定是拦截 j 的后面的系统
******************************************************/
#include<stdio.h>
#include<algorithm>
using namespace std;const int maxn = 1000+10;
const int INF = 30000+10;int a[maxn]; //存导弹的高度
int dp[maxn]; //d[i] 表示第 i 个导弹是被第 dp[i] 个拦截系统拦截的int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
dp[i] = 1;
} for(int i = 0; i < n; i++)
{
for(int j = 0; j < i; j++)
if(a[i] > a[j])
dp[i] = max(dp[i], dp[j]+1);
} int ans = 0;
for(int i = 0; i < n; i++)
ans = max(ans, dp[i]);
printf("%d\n", ans);
}
return 0;
}

Dp之 O(nlogn) 二分查找:

D Accepted 236 KB 0 ms C++ 959 B

#include<stdio.h>
#include<algorithm>
using namespace std;const int maxn = 1000+10;int a[maxn]; //导弹高度
int h[maxn]; // h[i] 表示第 i 个系统目前拦截的高度int find(int h[], int len, int ha) //返回 index , 数组h[] 中, 第一个h[index] >= ha
{
int left = 0;
int right = len; while(left <= right)
{
int mid = (left+right) / 2; if(ha > h[mid]) left = mid+1;
else if(ha < h[mid]) right = mid-1;
else return mid;
}
return left;
}int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
} h[0] = -1;
h[1] = a[0];
int len = 1; for(int i = 1; i < n; i++)
{
int index = find(h,len, a[i]);
h[index] = a[i];
//printf("test : h[%d] = %d\n", index, h[index]);
if(index > len)
len = index;
}
printf("%d\n", len);
}
return 0;
}

Dp O (nlogn)之直接调用upper查找

D Accepted 236 KB 0 ms C++ 814 B 2013-08-05 11:34:41

#include<stdio.h>
#include<algorithm>
using namespace std;const int maxn = 1000+10;
const int INF = 30000+10;
int a[maxn]; //导弹高度
int h[maxn]; // h[i] 表示当前第 i 个系统拦截的高度int main()
{
int n;
while(scanf("%d", &n) != EOF)
{
for(int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
// h[i] = INF;
} h[0] = -1;
h[1] = a[0];
int len = 1; for(int i = 1; i < n; i++)
{
int index = upper_bound(h,h+len+1,a[i])-h; //保证 h[index] 是数组 h 中第一个 >= a[i] 的
h[index] = a[i];
//printf("test: h[%d] = %d\n", index, h[index]);
if(index > len)
len = index;
}
//for(int i = 0; i <= n; i++) printf("%d ", h[i]); printf("\n");
printf("%d\n", len);
}
return 0;
}

上一篇: 【Lombok】了解
下一篇: Akka Essentials - 2
相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,077
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,552
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,401
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,176
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,813
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,896