hdu 1257 最少拦截系统

探讨了一种导弹拦截系统的算法实现,通过分析导弹来袭高度数据,计算最少需要的拦截系统数量。

最少拦截系统

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
2
 

 

这个题目没有明确的输入的上限,我的考虑建立一个数组,记录已经投入使用的导弹拦截系统当前拦截上限,为了使得使用的拦截系统最少,自然是要考虑使用与当前高度最接近的系统拦截(应该是贪心算法),输入上限未知,于是还是决定冒险偷懒,每一步取完之后都重新排序一次(其实只要对一个元素重新定位,如果超时的话可以写自己的版本,多一个log n因子一般也影响不大),当未能匹配时启用一套新的系统
top++,以下为我的程序:
#include <iostream>
#include <string>
#include <algorithm>
#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<t;i++)
  {
   cin>>height;
   arrange(height);
  }
  cout<<top+1<<endl;
 }
  
 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 <stdio.h>
#include <algorithm>
#include <iostream.h>
#include<stdlib.h>
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<have)
   {
    max=have;
    for(int i=0;i<n;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<n;i++)
             cin>>h[i];
      while(n)
   {
             for(int j=0;j<n;j++)
        option[j]=0;
       max=0;
             Cal(0,h[0],n,0);
       int temp=n;
       n=0;
       for(j=0;j<temp;j++)
    {
        if(option[j]==0)
         h[n++]=h[j];
    }
       count++;
   }
      cout<<count<<endl;
  }
}

  最后取所有d[i]的最大值就行了。 觉得比较好的策略 复杂度O (n2),考虑了所有的情况,从后向前计算可以理解是如果导弹从该结点开始打能打到多少,从前向后计算思路其实也一样(更符合我们的思维方式),但是没办法利用前面子问题的结果,每一个点都要对其后面点的代价算一遍,子问题重叠开销太大,加深对动态规划的理解,由于最大起点并不确定,所以需要最后一次搜索过程
但不知道 所谓的最少 递减子序列的求法能不能一次次从起始位置探查,一趟趟删除直到序列为空,直觉觉得不是整体最优 有待尝试
简单的从头一次探查应该不行(就是没回溯的一次决策),但是每次都遵循求解最长不降子序列的算法应该可行
应该求最长 不降 子序列。这样的长度才是 最少需要的 套数,因为这个序列中的任何两个导弹都不能共用一个拦截系统   ,而且其余的导弹 都能和这个最长序列中的某个导弹分为同一组
一下子有点困惑了,采用最简单的贪心算法也能AC,就是每一趟都只根据当前元素的高度一次决定 绝不回溯,最终返回遍历趟数ok,还是觉得有点奇怪。。
代码如下:
#include <iostream>
using namespace std;
struct node
{
 int h;
 int tag;
};
node rec[10000];
int find(int n)
{
 for(int i=0;i<n;i++)
  if(rec[i].tag)break;
 return i;
}
int main()
{
 int t;
 while(cin>>t)
 {
  for(int i=0;i<t;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<t;i++)
    if(rec[i].h<=allow&&rec[i].tag)
    {
     rec[i].tag=0;
     allow=rec[i].h;
    }
  }
  cout<<count<<endl;
 }
 return 0;
}
### HDU操作系统中添加系统调用的实验相关资料 在操作系统的实验课程中,系统调用是理解操作系统运行机制的重要组成部分[^1]。通过添加新的系统调用,学生可以深入学习内核与用户空间之间的交互方式、系统调用的实现原理以及如何设计高效的接口。以下是一个典型的实验报告框架和代码示例,帮助理解和实现系统调用。 #### 实验目标 实验的目标是让学生掌握系统调用的基本原理,并通过实际编程实现一个新的系统调用。这包括修改内核代码、编写用户空间测试程序以及验证系统调用的功能正确性。 #### 系统调用的基本概念 系统调用是操作系统提供给用户程序的一组接口,允许用户程序请求内核提供的服务。这些服务通常涉及资源管理、文件操作、进程控制等。HDU的操作系统实验中,要求学生通过编写代码实现一个自定义的系统调用。 #### 实验步骤概述 以下是实现系统调用的主要技术点: - 修改内核源码以注册新的系统调用。 - 编写用户空间测试程序以调用新增加的系统调用。 - 验证系统调用的功能是否符合预期。 #### 示例代码:添加一个简单的系统调用 假设需要添加一个名为 `sys_hello` 的系统调用,其功能是在内核日志中打印一条消息。 ##### 内核代码修改 在内核源码中,需要完成以下操作: 1. **定义系统调用函数** 在 `kernel/sys.c` 文件中添加如下代码: ```c #include <linux/kernel.h> #include <linux/syscalls.h> SYSCALL_DEFINE0(sys_hello) { printk(KERN_INFO "Hello from the kernel!\n"); return 0; } ``` 2. **更新系统调用表** 在 `arch/x86/entry/syscalls/syscall_64.tbl` 文件中添加一行,指定系统调用号及其名称: ``` 549 64 sys_hello sys_hello ``` 3. **重新编译内核** 使用适当的工具链重新编译内核并加载到实验环境中。 ##### 用户空间测试程序 编写一个简单的 C 程序来调用新添加的系统调用: ```c #define _GNU_SOURCE #include <unistd.h> #include <stdio.h> #include <sys/syscall.h> #include <errno.h> #define __NR_sys_hello 549 // 对应系统调用号 int main() { long ret = syscall(__NR_sys_hello); if (ret == -1) { perror("System call failed"); return errno; } printf("System call executed successfully.\n"); return 0; } ``` #### 注意事项 - 确保系统调用号未被其他系统调用占用。 - 测试程序需要使用 `syscall` 函数直接调用新增加的系统调用。 - 在实验报告中详细记录每一步的实现过程及遇到的问题。 #### 实验报告模板 1. **实验目的**:描述实验目标和学习重点。 2. **实验环境**:列出使用的硬件和软件环境。 3. **实验内容**:详细说明系统调用的实现过程。 4. **实验结果**:展示测试程序的输出结果,并附上截图或日志。 5. **总结与思考**:总结实验收获,并提出改进建议。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值