最少拦截系统
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 2686 Accepted Submission(s): 905
Problem Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.
Input
输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)
Output
对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
Sample Input
8 389 207 155 300 299 170 158 65
Sample Output
这个题目没有明确的输入的上限,我的考虑建立一个数组,记录已经投入使用的导弹拦截系统当前拦截上限,为了使得使用的拦截系统最少,自然是要考虑使用与当前高度最接近的系统拦截(应该是贪心算法),输入上限未知,于是还是决定冒险偷懒,每一步取完之后都重新排序一次(其实只要对一个元素重新定位,如果超时的话可以写自己的版本,多一个log n因子一般也影响不大),当未能匹配时启用一套新的系统
top++,以下为我的程序:
#include
#include
#include
#define MAX 100000000
using namespace std;
int height[10000];
int top;
void arrange(int n)
{
sort(height,height+top+1);
for(int i=0;i<=top;i++)
if(height[i]>=n)
{
height[i]=n;
break;
}
if(i==top+1)//引入新的导弹系统
{
top++;
height[top]=n;
}
}
int main()
{
int t;
while(cin>>t)
{
top=0;
height[0]=MAX;//初始可以阻挡任何高度
int height;
for(int i=0;i {
cin>>height;
arrange(height);
}
cout< }
return 0;
}
PS:虽然简单,但还是有几个小问题以后要注意
1,输入的时候有时还是把==敲成 =;
2,声明了top的全局版本,在main内部又声明了一个局部版本,输出为局部版本,全局被屏蔽,不调试很难发现
发现网上有过一个类似的问题,也综合在这里:
问题描述:
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够达到任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在使用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹依次飞来的高度(雷达给出高度数据是不大于30000的正整数),计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹系统。
样例:
Input: 389 207 155 300 299 170 158 65
Output: 6(最多拦截导弹数)
2(要拦截所有导弹最少要配备的系统数)
运行示例:
INPUT: 300 250 275 252 200 138 245
OUTPUT: 5 2
INPUT: 181 205 471 782 1033 1058 1111
OUTPUT: 1 7
INPUT: 465 978 486 324 575 384 278 214 657 218 445 123
OUTPUT: 6 4
INPUT: 236 865 858 565 545 445 455 656 844 735 638 652 569 714 845
OUTPUT: 6 7
简而言之:就是求最长递减数列和求最少递减数列的问题。
但二者基于的考虑完全是不同的,整体最优和局部最优是不同的,出于考虑的不同使得得出的布局完全不同,因此在我的程序中如果对每一套拦截系统增加一个计数器并不能解决第一问的问题。
第一感觉是用搜索算法,但要好好设计减枝策略,一个dp想法不错,从后向前计算(自底向上,这样没有内在的牵制,只需要关心值的关系)
设有n枚导弹,d[n] = 1;
对于第i枚导弹,
for j = i+1 -> n
{
d[i] = {1, 1+d[j](if j的高度小于i的高度)}
}
搜索版本,边搜索边记录探查序列,到最后一个结点时决定是否copy路径:
我用每次都用搜索剪枝法将最长的不升子序列求出来
我不懂你的dp什么意思,如果把搜索剪枝法换成dp,复杂度少了
不过还是贪心算法,换汤不换药
[code]
#include
#include
#include
#include
using namespace std;
int option[1000],op[1000];
int h[1000],n,max;
void Cal(int k,int height,int remain,int have)
{
if(k==n)
{
if(max {
max=have;
for(int i=0;i option[i]=op[i];
return;
}
}
if(have+remain-1>max)
{
op[k]=0;
Cal(k+1,height,remain-1,have);
}
if(h[k]<=height&&have+remain>max)
{
op[k]=1;
Cal(k+1,h[k],remain-1,have+1);
}
}
int main()
{
while(cin>>n)
{
int count=0;
for(int i=0;i cin>>h[i];
while(n)
{
for(int j=0;j option[j]=0;
max=0;
Cal(0,h[0],n,0);
int temp=n;
n=0;
for(j=0;j {
if(option[j]==0)
h[n++]=h[j];
}
count++;
}
cout< }
}
最后取所有d[i]的最大值就行了。 觉得比较好的策略 复杂度O (n2),考虑了所有的情况,从后向前计算可以理解是如果导弹从该结点开始打能打到多少,从前向后计算思路其实也一样(更符合我们的思维方式),但是没办法利用前面子问题的结果,每一个点都要对其后面点的代价算一遍,子问题重叠开销太大,加深对动态规划的理解,由于最大起点并不确定,所以需要最后一次搜索过程
但不知道 所谓的最少 递减子序列的求法能不能一次次从起始位置探查,一趟趟删除直到序列为空,直觉觉得不是整体最优 有待尝试
简单的从头一次探查应该不行(就是没回溯的一次决策),但是每次都遵循求解最长不降子序列的算法应该可行
应该求最长 不降 子序列。这样的长度才是 最少需要的 套数,因为这个序列中的任何两个导弹都不能共用一个拦截系统 ,而且其余的导弹 都能和这个最长序列中的某个导弹分为同一组
一下子有点困惑了,采用最简单的贪心算法也能AC,就是每一趟都只根据当前元素的高度一次决定 绝不回溯,最终返回遍历趟数ok,还是觉得有点奇怪。。
代码如下:
#include
using namespace std;
struct node
{
int h;
int tag;
};
node rec[10000];
int find(int n)
{
for(int i=0;i if(rec[i].tag)break;
return i;
}
int main()
{
int t;
while(cin>>t)
{
for(int i=0;i cin>>rec[i].h,rec[i].tag=1;
int count=0;
while(find(t)!=t)
{
count++;
int start=find(t);
rec[start].tag=0;
int allow=rec[start].h;
for(int i=start;i if(rec[i].h<=allow&&rec[i].tag)
{
rec[i].tag=0;
allow=rec[i].h;
}
}
cout< }
return 0;
}