zoj 1025 Wooden Sticks

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=25 


 引用cc98 watashi 大牛的话: 
其实题目的意思就是把所有元素分为最少的堆数,每堆有l<=l' and w<=w' 按l排序后(l相等则按w),问题转化为把所有元素分为最少的堆数,每堆有w<=w'(l<=l' 显然成立) 即已知一个数列,要求最少用多少个不下降序列完全覆盖
可以证明不下降序列完全覆盖数就是最长下降子列的长度(记为L): 显然覆盖数不能比L小,否则由抽屉原理,必然有下降子列中两元素(a < b)在同一不下降须列中(a <= b),这是不可能的 由覆盖数可以取得L,而序列的每个元素在不同堆中,然后每次将元素“贪心”地分在堆中,这个过程和dp地求最长下降子列很像,可以构造解,也可以反证如果不能分号,与下降子列长度为L矛盾。
于是先将数列按照l,w的顺序进行快排,然后在求出w序列中的最长递减序列的长度就可以了.
最长单调子序列及其计数问题

方法一:
设数列长度是n+1,那么很显然最长子序列可以这么来解决:假设已求出a[0]到a[n-1]的最长子序列的长度为s,如果a[n]小于这个子序列的最后一个元素,那么数列a的最长递减序列长度就是s+1。所以类似的对于每个元素都可以这么来求。设数组b,其中b[i]表示从a[0]到a[i]且终止于a[i]的最长递减序列的长度。则有:
b[i] = MAX { b[t] + 1 | a[t] > a[i], 0 <= t < i },如果存在t的话;否则b[i] = 1。
这样,对输入数列从前到后地扫描一遍,将数组b填满,然后b[i]的最大值就是最长递减序列的长度。
算法很容易实现,时间复杂度为O(n^2)。

我写的代码:
int b[SIZE]; b[0] = 1; for(int i = 1; i != n; ++i) { int max = 1; for(int j = 0; j < i; ++j) if(st[i].w < st[j].w && b[j] + 1 > max) max = b[j] + 1; b[i] = max; } cout << *max_element(b, b + n) << endl; 

方法二:

  for(int i = 1; i != n; ++i) { left = 1; right = len; while(left <= right) { mid = (left + right) >> 1; if(a[i] < b[mid]) left = mid + 1; else right = mid - 1; } b[left] = st[i].h; if(left > len) ++len; } cout << len << endl;

 

最后,贴上我AC的O(n2)的代码
#define SIZE 20001 #include<iostream> #include<algorithm> using namespace std; struct Sticks { int l; int w; }; int cmp(struct Sticks x, struct Sticks y) { if(x.l == y.l) return x.w < y.w; else return x.l < y.l; } int main() { int T; cin >> T; while(T--) { int n, x, y; Sticks st[SIZE]; cin >> n; for(int i = 0; i != n; ++i) { cin >> x >> y; st[i].l = x; st[i].w = y; } sort(st, st + n, cmp); int b[SIZE]; b[0] = 1; for(int i = 1; i != n; ++i) { int max = 1; for(int j = 0; j < i; ++j) if(st[i].w < st[j].w && b[j] + 1 > max) max = b[j] + 1; b[i] = max; } cout << *max_element(b, b + n) << endl; } return 0; }

 

你可能感兴趣的:(zoj 1025 Wooden Sticks)