现在的位置: 首页 > 综合 > 正文

poj1065:Wooden Stick – 偏序定理与最长递增子序列

2017年10月27日 ⁄ 综合 ⁄ 共 1471字 ⁄ 字号 评论关闭

poj1065

这篇文章讲的很好了,我把自己理解的过程再记录一下。

回想了一下好像与最长递增子序列有些联系。有木有联系这个坑待填。

--------------------------

之前曾经分析过最长递增子序列(那篇Blog还在施工中...-->现在施工完毕了)

========================

先补充一些偏序方面的知识:

偏序关系:

偏序是在集合X上的二元关系≤,它满足自反性、反对称性和传递性。即,对于X中的任意元素a,b和c,有:

自反性:a≤a;
反对称性:如果a≤b且b≤a,则有a=b;

传递性:如果a≤b且b≤c,则a≤c 。

带有偏序关系的集合称为偏序集。

当a与b之间存在a≤b或b≤a时,称a与b可比。

一个反链A是X的一个子集,它的任意两个元素都不可比。

 一个链C是X的一个子集,它的任意两个元素都可比。

单个元素的偏序集是链。这我自己的推论,应该对吧。。

偏序定理:

定理1 令(X,≤)是一个有限偏序集,并令r是其最大链的大小。则X可以被划分成r个但不能再少的反链。
 其对偶定理称为Dilworth定理:
定理2 令(X,≤)是一个有限偏序集,并令m是反链的最大的大小。则X可以被划分成m个但不能再少的链。

回到1065,这题就是要找出树枝集合的最小数目的划分,每个划分的子集都是链。链内部按顺序排列链内部没有setup开销,链之间有setup开销。

这可以保证机器的setup时间最少。由于上面的定理,可以知道,这种最小数目的划分为最长反链的长度。也就是要找出一个集合,这个集合保证包含了最多的两两之间不可比的元素【最长的反链】。

考虑本题具体条件下的一个反链:

{a,b,c,d};a,b,c,d都不可比。

假如对a,b,c,d按l排序的顺序为 a.l < c.l = d.l < b.l;由于不可比,a.w > c.w > d.w > b.w。

方法1:那本题就转化为,在l升序排列下求w的最长递减子序列。

补上本法的代码:

#include <stdio.h>
#include <algorithm>
#define N 5000
using namespace std;
typedef struct _ws{
	int l;
	int w;
}ws;
ws s[N];
bool cmp(ws a,ws b){
	return a.l < b.l;
}
int main(){

	int t,n,i,j,Len;
	scanf("%d",&t);
	while(t > 0){
		scanf("%d",&n);
		for(i = 0; i < n; i++)
		{
			scanf("%d%d",&s[i].l,&s[i].w);
		}
		sort(s,s+n,cmp);
		int D[N+1];//可改为O(nlgn)  长度为n的DS(decrease subsequence)的最大结尾
		D[1] = s[0].w;j = 1;Len = 1;//求w的最长严格递减子序列
		while(j < n){
			if(s[j].w < D[Len]){
				D[++Len] = s[j].w;
			}
//			else if(s[j].w == D[Len]){
//				;//do nothing
//			}
			else{
				int k = Len;
				while(k > 0){
					if(s[j].w < D[k])
						break;
					k--;//O(n^2)
				}
				D[k+1] = s[j].w;
			}
			j++;
		}
		printf("%d\n",Len);
		t--;
	}
	return 0;
}

poj1065测试通过.

方法2:另外也想出了一个方法用图的方法解决之。

先根据每个点的关系做一个哈塞图,接着按类似拓扑排序的“减治法”找出所有的反链,接着得出最长的反链元素个数+1即是本题的答案.

==以后有空再写这个方法的代码。

抱歉!评论已关闭.