挑战程序设计竞赛2.3:Wooden Sticks POJ - 1065
There is a pile of n wooden sticks. The length and weight of each stick are known in advance. The sticks are to be processed by a woodworking machine in one by one fashion. It needs some time, called setup time, for the machine to prepare processing a stick. The setup times are associated with cleaning operations and changing tools and shapes in the machine. The setup times of the woodworking machine are given as follows:
(a) The setup time for the first wooden stick is 1 minute.
(b) Right after processing a stick of length l and weight w , the machine will need no setup time for a stick of length l' and weight w' if l <= l' and w <= w'. Otherwise, it will need 1 minute for setup.
You are to find the minimum setup time to process a given pile of n wooden sticks. For example, if you have five sticks whose pairs of length and weight are ( 9 , 4 ) , ( 2 , 5 ) , ( 1 , 2 ) , ( 5 , 3 ) , and ( 4 , 1 ) , then the minimum setup time should be 2 minutes since there is a sequence of pairs ( 4 , 1 ) , ( 5 , 3 ) , ( 9 , 4 ) , ( 1 , 2 ) , ( 2 , 5 ) .
(a) The setup time for the first wooden stick is 1 minute.
(b) Right after processing a stick of length l and weight w , the machine will need no setup time for a stick of length l' and weight w' if l <= l' and w <= w'. Otherwise, it will need 1 minute for setup.
You are to find the minimum setup time to process a given pile of n wooden sticks. For example, if you have five sticks whose pairs of length and weight are ( 9 , 4 ) , ( 2 , 5 ) , ( 1 , 2 ) , ( 5 , 3 ) , and ( 4 , 1 ) , then the minimum setup time should be 2 minutes since there is a sequence of pairs ( 4 , 1 ) , ( 5 , 3 ) , ( 9 , 4 ) , ( 1 , 2 ) , ( 2 , 5 ) .
Input
The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case consists of two lines: The first line has an integer n , 1 <= n <= 5000 , that represents the number of wooden sticks in the test case, and the second line contains 2n positive integers l1 , w1 , l2 , w2 ,..., ln , wn , each of magnitude at most 10000 , where li and wi are the length and weight of the i th wooden stick, respectively. The 2n integers are delimited by one or more spaces.
Output
The output should contain the minimum setup time in minutes, one per line.
Sample Input
3 5 4 9 5 2 2 1 3 5 1 4 3 2 2 1 1 2 2 3 1 3 2 2 3 1
Sample Output
2 1 3
这道题看起来束手无策,但是如果你知道组合数学中神奇的狄尔沃斯定理的话,一切就迎刃而解。
狄尔沃斯定理: 对偏序集<A,≤>,设A中最长链的长度是n,则将A中元素分成不相交的反链,反链个数至少是n。
对于反链:设<A>是一个偏序集合,在A的一个子集中,如果每两个元素都是有关系的,则称这个子集为链。在A的一个子集中,如果每两个元素都是无关的,则称这个子集为反链。
证明由于鄙人数学垃圾,大家可以去看看百度百科的证明。
题意要求满足对于后面的x,y和前一个x',y'有x >= x'且 y >= y',那么从后往前来看就是一个不上升序列,因此题意是要求我们求得这个最长不上升子序列的最小个数。
那么在这里,反链就是最长上升子序列,而最长上升子序列的长度也就是最长不上升子序列的最小个数,也就是我们要求的答案,注意长度和序列个数不一样。
我们可以先排序,因为我们要得到最长上升子序列的长度并且我们要从后往前遍历,就应该按照从小到大排序,对于二维,我们也可以采取排x,这样可以保证,倒序遍历的时候x一定能小于等于之前的,也就是x不会有问题,我们就只要比较y即可。
在我们倒序进行排列的时候,我们先把建立一个数组dp(该数组为最长上升序列)初始化为INF,然后把y与最小的大于等于y的值找出(因为等于在上升序列中不会改变序列长度,得要是大于),那么把y与其替换,这样可以使得如此操作后找到最长的上升子序列的个数(也就是数组的个数,因为这样替换使得如果没有比y大的数,那么就会找到INF,将其替换,这样序列的长度就多了一个;如果有比y大的数,保证该数组的原本第一个比y大的值可以变小,但是保持dp的递增的序列不变,如果后面存在现有的序列还大的递增序列,那么就会被替换,这样循环就能找到最长的上升序列。
(结合白书P65仔细想想为什么这样操作就能找到最大上升子序列)
找到了之后就好办了,求出该序列的长度,就能得到最长不上升序列的个数,也是题目要求的答案。
AC代码:
#include <stdio.h> #include <algorithm> using namespace std; struct Node{ int x; int y; friend bool operator <(Node x, Node y){ return x.x < y.x; } }sticks[5005]; const int INF= 0x3fffffff; int dp[5005]; int main(void) { int t, n; scanf("%d", &t); for(int times = 0; times < t; times++) { scanf("%d", &n); fill(dp, dp + 5005, INF); for(int i = 0; i < n; i++) { scanf("%d %d", &sticks[i].x, &sticks[i].y); } sort(sticks, sticks + n); for(int i = n - 1; i >= 0; i--) { //lower_bound就是找到在当前序列中第一个大于等于sticks[i].y的位置,它是一个地址减去dp的首地址就得到了该位置的dp数组的下标,实际上这行和下一行可以合并为*lower_bound(dp, dp + n, sticks[i].y) = sticks[i].y的,为了怕指针难看懂就这样写了。 int p = lower_bound(dp, dp + n, sticks[i].y) - dp; dp[p] = sticks[i].y; } printf("%d\n", lower_bound(dp, dp + n, INF) - dp);//最后最长上升子序列的长度就是最长不上升子序列的最小个数,也就是答案 } }