EOJ1765 Nested Dolls 最长上升子序列

本题我试过很多方法,最开始的思路是,一个娃娃可以套另一个娃娃,这种偏序关系可以建图。找出图中最长的路径,然后把那些点删掉,再找出最长的路径,删掉,直至图中没有点,删除的次数就是答案,可是这样会超时。第二个想法差不多,按照w+h的值递增的顺序对娃娃排序,然后用nlogn算法找出lis数组。然后利用lis数组不断的删除最长的子序列。其实和第一种思路差不多,但是还是超时。第三种就是按照w+h递减的顺序排序,然后从前往后找的第一个娃娃肯定就是答案中放在最外面的,不断的往后扫描,找到第一个能装进去的,这个娃娃的规格肯定和他最接近(贪心),就删除它,一直扫描到末尾。不断重复直至删除所有娃娃,复杂度n^2,还是超时。最后看了网上的博客,才能ac。


假设最后的答案是n,那么所有的娃娃都可以被套进那n个最大的娃娃中,并且这n个娃娃有一个特点,就是它们不能相互嵌套。那么我们是否可以猜测输入的娃娃中两两不能相互嵌套的娃娃数目就是答案呢?事实证明这个想法是正确的。如果答案比n还要小,那么就意味着我们要将两个互相不能嵌套的娃娃互相嵌套,太荒谬了!如果答案比n大,假如是n+1。那么就有n+1个不能相互嵌套的娃娃,但是我们算出来的是n,不可能是n+1。即n就是最后的答案

那么如何找到两两不能相互嵌套的娃娃数目?首先按照w从大到小对娃娃排序,然后对于这些娃娃的h构成的序列,找出它的最长上升子序列,这个子序列的长度就是两两不能相互嵌套的娃娃数目。为什么呢因为最长上升子序列中,任意两个娃娃a,b都满足a.w>b.w  a.h<b.h 所以他们不能相互嵌套。

下面是代码具体实现:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <fstream>
#include <istream>
#include <ostream>
#include <complex>
#include <cstring>
#include <utility>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <string>
#include <cctype>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <new>
#include <set>
#include <map>

using namespace std;

const int maxn = 20000 + 5;
const int INF = 0x3f3f3f3f;
typedef long long int LL;
typedef vector<int> vec;
struct doll
{
    int w, h;
    bool operator < (const doll &rhs) const
    {
        //这里一定要注意如果w相等,要按照h递增的顺序
        //参考 50 40,40 30,40 20,30 10这组数据
        if (w != rhs.w)
            return w > rhs.w;
        else return h < rhs.h;
    }
};
doll a[maxn];
int lis[maxn], len;
int n;

void Binary_insert(int d)//二分查找第一个大于d的元素(注意不是大于等于),将其替换成d
{
    int l = 1, h = len, m;
    while(l <= h)
    {
        m = (l + h) / 2;
        if(d < lis[m])
            h = m - 1;
        else l = m + 1;
    }
    lis[l] = d;
}

int main()
{
    //freopen("1.txt", "r", stdin);
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int n;
        scanf("%d", &n);
        for (int i=0; i<n; i++)
            scanf("%d%d", &a[i].w, &a[i].h);
        sort(a, a+n);
        len = 0;
        lis[++len] = a[0].h;
        for (int i=1; i<n; i++)//求lis长度
        {
            if (a[i].h >= lis[len])//注意是>=不是大于
                lis[++len] = a[i].h;
            else
                Binary_insert(a[i].h);
        }
        printf("%d\n", len);
    }
    return 0;
}
本题用的不是纯正的LIS,因为我们求的不是严格的单调递增,而是非递减序列。计算时>=末尾元素值的时候就可以接在末尾,<末尾时,要在lis数组中找到第一个大于它的元素,而不是大于等于,之前我用lower_bound函数一直wa。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值