挑战程序设计竞赛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 ) .

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);//最后最长上升子序列的长度就是最长不上升子序列的最小个数,也就是答案
	}
}
			

  

 

  



posted @ 2020-01-26 12:59  funforever  阅读(231)  评论(0编辑  收藏  举报