标签:
Description
Input
Output
Sample Input
4 3 20 30 40 50 30 40 4 20 30 10 10 30 20 40 50 3 10 30 20 20 30 10 4 10 10 20 30 40 50 39 51
Sample Output
1 2 3 2
题目大意:
有n个盒子,知道宽和高,如果第一个盒子的宽和高是w1,h1,第二个的宽和高是w2,h2,如果w1<w2 && h1<h2 ,那么第二个盒子就能装下第一个盒子,求最终剩下多少个盒子
思路分析:
这道题目和装娃娃的题目有点像,排序后对h做最长上升子序列,输出的最大长度即可
首先肯定能想到的是 贪心,不停遍历,不停更新。。复杂度 n*n (超时!!),然后又想到和以前做的题类似,有想到 二分图匹配之最小路径覆盖。。这个题数据20000个点,铁定超时。。。。。。。。
然后就开始往dp上思考,首先得排序,这个很关键,按照长从大到小排序,再按照高从小到大排序,一开始还以为只要都按照从大到小就可以,但是对于长相同的盒子此排序就不对了,甚是头大啊,经过了解学习,发现了一个特别的牛的思路,大牛果然牛!
具体排序方法如下:
按照 长 对盒子排序(由大到小),然后对于所有盒子的高组成的一个序列,求最长单调递增子序列的个数,这就是问题的解!仔细想一下,列出求的单调递增序列对应的盒子,发现他们任意两两都是不能嵌套的。然后其余的盒子都可以嵌套进入这些盒子中!这个想法太神奇了……对于求最长单调递增子序列,要是动态规划复杂度n^2,还是要超时……。这里再利用二分查找,复杂度O(n*logn),就行了……
附上代码:
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #define INF 0x3f3f3f3f using namespace std; struct A { int w,h; }point[20010]; bool cmp(A a,A b) // 定义排序方式,按照 w 从大到小 , w 相等的时候 h 从小到大的方式排序( h 的这点很重要,h 的从小到达排序,解决了 当 w 相等后需要多用一个盒子的情况,因为 h 从小到大排序,那么最长上升子序列的长度就会增加) { if(a.w == b.w) return a.h < b.h; return a.w > b.w; } int dp[20010]; int main() { int t; cin >> t; while(t--) { int m; cin >> m; for(int i = 0;i < m;i++) { scanf("%d%d",&point[i].w,&point[i].h); } sort(point,point + m,cmp); fill(dp,dp+m,INF); // 初始化 dp 数组,初始化为最大值 int ans = 0; // 下面是求最长上升子序列的过程,该解法是在《挑战程序设计》上讲到的。 for(int i = 0;i < m;i++) // 利用 nlog(n) 的时间复杂度求解最长上升子序列 { *upper_bound(dp,dp+m,point[i].h) = point[i].h; // 这里是用了 STL 的函数,其原理是一个二叉树,时间复杂度就和二分的一样,另外这点只能用 upper_bound()函数 ,而不能用按照《挑战程序设计》上讲解的时候用的lower_bound()函数,具体什么原因,读者可以自己手算一下用二分法时修改的是哪一个值。 } printf("%d\n", lower_bound(dp,dp+m,INF) - dp); // 输出最长上升子序列的长度 } return 0; }
UVA 11368 & POJ 3636 & HDU 1677 Nested Dolls(贪心 + 二分LIS)
标签:
原文地址:http://blog.csdn.net/xia842655187/article/details/51332914