Dilworth定理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dilworth定理相关的知识,希望对你有一定的参考价值。

今天早上准备看一波uestc的dp,看到第一道例题的时候发现我竟然不会QAQ,心想清早看的第一题我都不会,甚是郁闷,然后又去百度百度……发现了一个Dilworth定理,然后一直怼一直怼。

 

结论:对于一个偏序集,最少的chain的个数等于最长antichain的长度,最少的antichain的个数等于最长chain的长度。

 

比如对于一个二元组,定义偏序关系"≤",当且仅当(a.i < b.i) && (a.j < b.j)时,a 与 b可比,但是会发现有些二元组是没法比较的,称之为偏序集。

与她相对的东西叫做全序集,就是任意两个元素都可以比较,还有就是良序集,有最小元素,比如正整数就是一个良序集。

 

看了半天QAQ,依然没有看懂证明,证明好久懂了,再回来补吧,接下来我讲一讲这个定理的实际的应用

 

一、

codevs 1044 导弹拦截:

题意:依次给出每个导弹飞来的高度,有一种反导弹装置可以将导弹击毁,但是中途过程中,反导弹装置的高度不能升高,求某一台装置最多击落多少个导弹?请问至少需要多少台装置可以拦截所有导弹?

 

题解:

chain : xi ≤ xj 当且仅当 (i <= j && h[i] >= h[j]) 

antichain : (h[i] < h[j] && i <= j) || (h[i] >= h[j] && i > j)

如果按照i <= j 排序,那么antichain中所有的元素h[i] < h[j],现在的问题就是求这个串最长上升子序列的长度。

 

代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 7;
int x[N], n = 1, d[N], ans1, ans2;

int main () {
	while (cin >> x[n]) n++;
	--n;
	memset (d, 127, sizeof d);
	for (int i = n; i >= 1; --i) {
		int p = upper_bound (d + 1, d + 1 + n, x[i]) - d;
		ans1 = max (ans1, p);
		d[p] = x[i];
	} 
	memset (d, 127, sizeof d);
	for (int i = 1; i <= n; ++i) {
		int p = lower_bound (d + 1, d + 1 + n, x[i]) - d;
		ans2 = max (ans2, p);
		d[p] = x[i];
	}
	cout << ans1 << endl << ans2; 
	return 0;
}

  

 二、

POJ 1065 Wooden Sticks

题意:有n个棍子,每个棍子都有长度和质量,现在有一台机器来处理这些棍子,机器开启需要一个单位的时间,但是比如现在处理到了第 i 根棍子,如果第i + 1 根棍子的长度和质量都大于等于第i根棍子那么将不会消耗时间,求处理完这些棍子的最少时间。

 

题解:

chain : xi ≤ xj 当且仅当 (xi.m <= xj.m && xi.w <= xj.w)

antichain : (xi.m <= xj.m && xi.w > xj.w) || (xi.m > xj.m && xi.w <= xj.w)

如果按照x.m从小到大排序,那么就应该求最长下降子序列,但是在m相等的情况下要使x.w 按照从小到大的顺序来排,因为这样才能在求子序列的时候避免错误情况。

 

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e5 + 7;
int kase, n, d[N];

struct node {int a, b;} x[N];

bool cmp (node x, node y) {
	if (x.a == y.a) return x.b < y.b;
	return x.a < y.a;
}

int main () {
	scanf ("%d", &kase);
	while (kase--) {
		scanf ("%d", &n);
		for (int i = 1; i <= n; ++i) 
			scanf ("%d%d", &x[i].a, &x[i].b);
		sort (x + 1, x + 1 + n, cmp);
		memset (d, 127, sizeof d);
		int ans = 0;
		for (int i = n; i >= 1; --i) {
			int p = lower_bound (d + 1, d + 1 + n, x[i].b) - d;
			ans = max (ans, p);
			d[p] = x[i].b;
		}
		cout << ans << endl;
	}
	return 0;
}

  

poj 3636 Nested Dolls

题意:有n个矩形,如果一个矩形的长宽都小于另一个矩形的长宽,那么这两个矩形可以嵌套在一起,问最少需要几个嵌套组合才能匹配完所有的矩形?

 

题解:

chain : xi ≤ xj 当且仅当 (xi.l < xj.l && xi.w < xj.w)

antichain :  (xi.l <= xj.l && xi.w >= xj.w) || (xi.l >= xj.l && xi.w <= xj.w)

如果按照x.l从小到大排序,那么就是求关于x.w的最长不上升子序列,但是注意的是当x.l相等的时候,要按照w从大到小排序。

 

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e5 + 7;
int kase, n, d[N];

struct node {int a, b;} x[N];

bool cmp (node x, node y) {
	return (x.a < y.a) || (x.a == y.a && x.b > y.b);
}

int main () {
	scanf ("%d", &kase);
	while (kase--) {
		scanf ("%d", &n);
		for (int i = 1; i <= n; ++i) 
			scanf ("%d%d", &x[i].a, &x[i].b);
		sort (x + 1, x + 1 + n, cmp);
		memset (d, 127, sizeof d);
		int ans = 0;
		for (int i = n; i >= 1; --i) {
			int p = upper_bound (d + 1, d + 1 + n, x[i].b) - d;
			ans = max (ans, p);
			d[p] = x[i].b;
		}
		cout << ans << endl;
	}
	
	return 0;
}

  

 

poj 1548 Robots

题意:在一个矩形区域内,一个机器人在左上角出发,每次只能向右向下进行移动,在移动中清理垃圾,一直到右下角,给出所有垃圾的位置,求至少要多少个机器人才能清理完所有垃圾?

 

题解:

chain : pi ≤ pj  (pi.x <= pj.x && pi.y <= pj.y)

antichain : (pi.x <= pj.x && pi.y > pj.y) || (pi.x > pj.x && pi.y <= pj.y)

如果按照p.x排序,那么也就是求关于y的最长下降子序列,需要注意的是当x 想等的时候按照要按照y从小到大排序。

 

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e5 + 7;
struct node {int x, y;} p[N];
int n, x, y, d[N];

bool cmp (node a, node b) {
	return a.x < b. x || (a.x == b.x && a.y < b.y);
}

int main () {
	while(true) {
		n = 0;
		while (scanf ("%d%d", &x, &y) != EOF) {
			if (x == 0 && y == 0) break;
			if (x == -1 && y == -1) return 0;
			p[++n] = (node) {x, y};
		}
		sort (p + 1, p + 1 + n, cmp);
		memset (d, 127, sizeof d);
		int ans = 0;
		for (int i = n; i >= 1; --i) {
			int pos = lower_bound(d + 1, d + 1 + n, p[i].y) - d;
			ans = max (ans, pos);
			d[pos] = p[i].y;
		}
		cout << ans << endl;;
	}
	return 0;
}

  

 

总结:

Dilworth,虽然不懂证明但是确实感受到了一波他的正确性,一般将她转化成DAG,在辨别可比性的时候,把它弄在平面上会比较直观,还有在写cmp的时候要想清楚……

 

以上是关于Dilworth定理的主要内容,如果未能解决你的问题,请参考以下文章

Dilworth定理

狄尔沃斯定理(Dilworth's theorem)

HDU 1257 最少拦截系统(Dilworth定理+LIS)

HDU3335 Divisibility Dilworth定理+最小路径覆盖

Dilworth定理证明

P1020 导弹拦截 /// DP Dilworth定理 LIS优化