hdu 1257 最少拦截系统(动态规划 / 贪心)

最少拦截系统

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 11667    Accepted Submission(s): 4585

Problem Description
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能超过前一发的高度.某天,雷达捕捉到敌国的导弹来袭.由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹.
怎么办呢?多搞几套系统呗!你说说倒蛮容易,成本呢?成本是个大问题啊.所以俺就到这里来求救了,请帮助计算一下最少需要多少套拦截系统.
 
Input
输入若干组数据.每组数据包括:导弹总个数(正整数),导弹依此飞来的高度(雷达给出的高度数据是不大于30000的正整数,用空格分隔)
 
Output
对应每组数据输出拦截所有导弹最少要配备多少套这种导弹拦截系统.
 
Sample Input
8 389 207 155 300 299 170 158 65
 
Sample Output
2013/5/16 15:12:00

  这道题实际上是重复求“最长非递增子序列”的过程。具体过程是重复倒序求最长非降(注意,实际上是非降)子序列,然后将每一次求出的最长非降序列剔除(将序列的每个值赋0),直到全部剔除(原序列全部变为0),剔除的次数就是至少要设置的拦截系统的数量。

  “倒序求最长非降子序列”即是求“最长非升子序列”(可以理解为“最长递减子序列”),可以想象,要求最长递减子序列,只要倒着求最长递增子序列就行了。为什么要这样求?因为动态规划里没有方法直接求最长递减子序列,只能通过最长递增子序列间接求最长递减子序列。  

  之前不太明白什么是“最长递增子序列”,从网上找了段解释看明白了:

  “所谓动态规划法就是从最后一个元素开始找递减子序列并记下该元素的位置和子序列的长度

  例如 对于题目中的  序列 9 8 2 1 7 5 3 4 3 2 1  

  最后一个元素 是 1 ,它的位置是  11  ,最长递减子序列的长度是 1

  倒数第二个元素 是  2 ,它的位置是 10,最长递减子序列的长度是 2 (递减子序列为:2 1)

  倒数第三个元素是 3,位置是9,最长递减子序列长度是3 (递减子序列为 3 2 1)

  倒数第四个元素是4 ,位置是8 ,最长递减子序列长度是4 (递减子序列为4 3 2 1)

  倒数五个元素是3 ,位置7,最长递减子序列长度是3           (递减子序列为 3 2 1,这里注意不是 4 3 2 1,因为4 比倒数第五个元素3 大所以实际上是  后面的 2  1 这个子序列加上倒数第五个元素3 组成的 3 2 1)

  ...

  一直求解出每个元素的最长递减子序列的长度

  然后找出所有元素中最长的子序列长度.例如本例上最长的子序列长度就是第一个元素长度为8

  最后就是根据该 长度 还原出该 子序列。

  还原方法就是 递减循环 子序列的长度 找出对应的元素 位置.再根据位置获取到对应的值

  例如 子序列长度为8  对应的为第一个元素  即 9

  递减后为 7 对应的为第二个元素 即 8

  以此类推 最后还原出得到序列9 8 7 5 4 3 2 1  ”

 

 1 #include <iostream>
 2 using namespace std;
 3 
 4 int main()
 5 {
 6     int n,a[10001];
 7     while(cin>>n){
 8         int i,index;
 9         for(i=1;i<=n;i++)   //输入数组
10             cin>>a[i];
11         //找到最长非升子序列
12 
13         int count=0;
14 
15         while(true){
16             for(i=1;i<=n;i++)       //检测
17                 if(a[i]!=0) break;  //全部为0,即将全部序列剔除之后退出循环
18             if(i>n) break;
19 
20             for(i=n;i>=1;i--){
21                 if(a[i]!=0){
22                     index=i;    //标记倒序第一个不是0的数
23                     break;
24                 }
25             }
26             for(i=n-1;i>=1;i--){
27                 if(a[i]==0) continue;
28                 if(i==index) continue;
29                 if(a[i]>=a[index]) {a[index]=0;index=i;} //比较当前数和标记的数。如果大于等于标记的上一个数,则将上次标记的数等于0,将本次标记。
30             }
31             a[index]=0;         //将最后标记的不是0的数赋值0
32 //            for(i=1;i<=n;i++)
33 //                cout<<a[i]<<' ';
34 //            cout<<endl;
35             count++;
36         }
37         cout<<count<<endl;
38 
39     }
40     return 0;
41 }

 


 

2014/1/20 16:21:27

  重新做了一遍,赫然发现贪心也可以AC。

  下面给出代码,仅供参考:

 1 #include <iostream>
 2 using namespace std;
 3 int main()
 4 {
 5     int n,a[1001];
 6     while(cin>>n){
 7         for(int i=1;i<=n;i++)
 8             cin>>a[i];
 9         int b[1001];    //记录每个拦截系统的当前拦截高度 
10         int count=0;
11         for(int i=1;i<=n;i++){
12             if(count==0)    //还没开始计数 
13                 b[++count] = a[i];
14             else{    //开始计数 
15                 int j;
16                 for(j=1;j<=count;j++)    //将记录的所有系统的拦截高度和当前导弹高度依次比较 
17                     if(a[i]<=b[j]){    //遇到小于某个拦截高度,代替那个高度 
18                         b[j] = a[i];
19                         break;
20                     }
21                 if(j>count){    //当前高度比记录的最小高度都要大,再建立一套拦截系统 
22                     count++;
23                     b[count] = a[i];
24                 }
25             }
26         }
27         cout<<count<<endl;
28     }
29     return 0;
30 }

 

Freecode : www.cnblogs.com/yym2013

posted @ 2014-01-20 16:21  Freecode#  阅读(1276)  评论(0编辑  收藏  举报