题目链接 POJ 1065-Wooden Sticks
题目大意
有 n 块木板, 每块都有各自的长
l 和宽 w 。
在加木板的时候,刚刚加工了一块长和宽分别为l 和 w ,加工的下一块长l′ ,宽 w′ , 如果 l<=l′ 并且 w<=w′ , 则可以直接加工,否则需要花费1单位时间来重新设置机器。加工第一块木板的时候必须设置机器。
求加工完 n 块木板所需要的最短的重置机器所花费的时间。
输入格式:
多组输入, 第一行表示测试数据的组数
接下来2行一组, 每组第一行为木板数, 第二行为l1,w1,...ln,wn
输出格式:
每组一行, 一个数字,表示最短的重置机器所花费的时间
题解:
将木板按照长来排序, 比如 ( 9 , 4 ) , ( 2 , 5 ) , ( 1 , 2 ) , ( 5 , 3 ) , ( 4 , 1 )
排序后为 ( 1 , 4 ) , ( 2 , 1 ) , ( 3 , 5 ) , ( 4 , 9 ) , ( 5 , 2 )
然后再找按照宽度 wi , 将上面的序列分成 x 个不下降的子序列。
比如 ( 1 , 4 ) , ( 3 , 5 ) , ( 4 , 9 ) 为一组, ( 2 , 1 ) , ( 5 , 2 ) 为一组,x=2
本题要求的答案就是 x 的最小值
根据Dilworth定理, 最小的不降序列的个数等于最长的下降序列的长度,所以这题只需要把木板按照长度排序, 然后再找宽度的最长下降子序列的长度。这里用了《挑战程序设计竞赛》里的二分的算法, 复杂度为O(nlogn) , 朴素的动态规划需要 O(n2) 。
非常巧妙的利用二分的算法。。最近时间紧, 日后补上详细讲解这题和以前做过的“导弹拦截”一样, 都用到了Dilworth定理。以前还推导过定理证明, 理解了好长时间。现在只记得定理的结论了。日后有空把定理的证明补上。
另外还有其他的证明方法, 参考: hankcs码农场
代码:
#include <iostream>
#include <algorithm>
#define MAXN 5010
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int, int> Pair;
Pair p[MAXN];
int f[MAXN], ans;
int n;
int main() {
int T;
cin >> T;
for (int t = 0; t < T; t++) {
cin >> n;
for (int i = 0; i < n; i++)
cin >> p[i].first >> p[i].second;
sort(p, p+n);
fill(f, f+n, INF);
for (int i = n-1; i >= 0; i--) {
*lower_bound(f, f+n, p[i].second) = p[i].second;
}
cout << (lower_bound(f, f+n, INF) - f) << endl;
}
return 0;
}