VJudge题目:https://cn.vjudge.net/contest/279505#problem/H
即HDU 1257 - 最少拦截系统:http://acm.hdu.edu.cn/showproblem.php?pid=1257
题意:输入N,在同一行 !依次! 提供N个数字,表示 !依次! 来袭的导弹的高度。
一个导弹拦截系统一开始能拦截30000高度的导弹,之后就不能拦截高度更大的导弹。
比如说,当233、233和666三个导弹 !依次! 来袭,当一个拦截系统拦截233之后就只能拦截第二个233,不能拦截更高的666;只能启用第二套拦截系统。
为什么强调 依次 ?要是导弹能排序再来袭还需要那么多套系统,一个系统统统拦截好了。
输入:注意是多组数据,需要scanf()!=EOF来判断文件是否结束。
示例:
Input:
8 389 207 155 300 299 170 158 65
Output:
2
我不明白这题为什么归入dp动态规划专题;这明明是贪心啊。
什么叫贪心?这道题绝对是个好栗子,贪心的典栗。先明白什么是贪心,可以理解动态规划的意义。
看懂题意后,假如让你来控制拦截系统,你会怎么做?
肯定是用能拦截当前导弹又最弱(拦截高度最低)的系统去拦截啊!不行再启用新系统呗。
换种说法是:
对于N个有循序的导弹,每次都选择拦截高度足够 又是 其中拦截高度最低 的一套去拦截。
也就是说:
对于N个有循序的问题,每次都选择能满足条件 又 损失最小/获利最大 的方案去解决问题。
这就是贪心算法的含义;在N个问题中对于每个子问题,总是都选择当前最有利的方法,以获得局部最优解。
为什么是局部最优解?因为这些问题是有循序的,必须解决这个问题才能获知下一个问题;
假如没有循序的话,贪心会非常吃亏。比如上面所说,导弹没有循序的话,排序好再拦截,永远只需要一套系统。
为什么贪心得不到这种更好的最优解?贪心是盲目的,不知道也不考虑以后的问题;
即使后面有对全局更有利的方法,也只会取当前最有利的一种而对全局未必最有利的方法。
所以经常说,贪心是局部最优解算法,动态规划是全局最优解算法。
所以贪心通常用来解决有序问题,相比之下解决无序问题的都是动态规划。一般都能保证最终答案是最优解。
(!很多有序问题还是得用动态规划来写的,也有无序问题用贪心的。关键还是在于 全局最优解 / 局部最优解 哪个是能符合题目要求的结果。)
到这里你应该对贪心有个大概的认识了。接下来把本题解决一下:
数组Greed[30003]存当前每套拦截系统的拦截高度。当读入一个导弹高度后,从数组第一个元素开始检查:
元素数值>=读入数值,说明能拦截,那么用读入数值给这个元素赋值,表明它被削弱了,然后退出;
没有元素的数值比它大,不能拦截,那么就地存下,表示新启用的系统的拦截高度如此。
这样可以保证,后面的数值总是>=前面的数值,而且每次都用数值最低的系统去拦截,从而控制损失最小。
最终看看数组中有多少个数值就可以了。
听说这题的解法叫最长递增子序列?可能这就是那个动态规划的解法吧;反正我用贪心搞定了。
代码如下:
1 #include <stdio.h> 2 #include <string.h> 3 4 int N=0; 5 int tmp=0; 6 int Greed[30003]={0}; 7 8 int main() 9 { 10 while(scanf("%d",&N)!=EOF) 11 { 12 memset(Greed,0,sizeof(Greed)); 13 14 for(int n=1;n<=N;n++) 15 { 16 scanf("%d",&tmp); 17 for(int i=0;;i++) 18 { 19 if(Greed[i]>=tmp) 20 { 21 Greed[i]=tmp; 22 break; 23 } 24 else if(Greed[i]==0) 25 { 26 Greed[i]=tmp; 27 break; 28 } 29 } 30 } 31 32 int i=0; 33 for(;Greed[i];i++); 34 printf("%d\n",i); 35 36 } 37 return 0; 38 }