http://poj.org/problem?id=1065
这个题目定义了一种偏序关系≤
(L1, W1)≤(L2, W2)当且仅当L1<=L2且W1<=W2
给的输入就是一个偏序集,目标就是把这个偏序集划分为一系列chain,并要求chain的个数尽可能少
根据Dilworth定理,最少的chain个数等于最大的antichain的大小
如何求最大的antichain的大小呢?
可以首先考虑antichain具有的性质:
(1)对antichain中任意两点P(L1, W1)和Q(L2, W2),L1不等于L2(否则P和Q就是可比较的,违反反链性质),W1不等于W2
(2)如果将反链中的点按照L值从小到大排列,那么W是递减的(考察任意两个相邻的点P(L1, W1)和Q(L2, W2),假设他们违反了递减性质,即W1<=W2,那么P和Q就是可比较的,违反了反链性质)
考虑到这两点,可以先对所有点按照L值从小到大排列,如果L值相同,W值小的在前(至于为什么W值小的在前,后面解释)
最长反链就是在这个序列中取一个最长的子序列,要求W值是递减的——这就是最长递减子序列问题了,可以用动态规划在O(n^2)时间内解决。
之所以排序时,L值相同时,要求W值小的在前,是因为这样就可以简单地归结为最长递减子序列问题,比如最简单的两个点的情况,P(2,3), Q(2,4),那么P应该在Q之前。如果Q在P之前,求最长递减子序列时会得到(2,4)(2,3),这就不是最长反链了
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int maxx = 5005;
struct Node
{
int l, w;
}a[maxx];
bool vis[maxx];
bool cmp(Node x, Node y)
{
if(x.l == y.l)
return x.w < y.w;
return x.l < y.l;
}
int dp[maxx];
int main()
{
int t, n;
cin>>t;
while(t--)
{
memset(vis, 0, sizeof(vis));
cin>>n;
for(int i = 0; i < n; ++i)
cin>>a[i].l>>a[i].w;
sort(a, a + n, cmp);
for(int i = 0; i < n; ++i)
dp[i] = 1;
for(int i = 1; i < n; ++i)
for(int j = 0; j < i; ++j)
if(a[i].w < a[j].w)
dp[i] = max(dp[i], dp[j] + 1);
int res = 1;
for(int i = 0; i < n; ++i)
res = max(res, dp[i]);
cout<<res<<endl;
}
return 0;
}