poj 1065-Wooden Sticks(利用Dilworth定理)

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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值