ZOJ 2319【二维最长递增子序列】

这是一道关于找出俱乐部成员中能参加聚会的最大人数的问题,条件是成员间不能有互相憎恨的情况。输入包含多组测试数据,每组数据包含每个成员的力量和美丽值,目标是邀请最多的人而不引发冲突。输出应包括最大邀请人数和对应的成员编号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【题目链接】:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2319

【原题】:

Beautiful People

Time Limit: 5 Seconds      Memory Limit: 32768 KB      Special Judge

The most prestigious sports club in one city has exactly N members. Each of its members is strong and beautiful. More precisely, i-th member of this club (members being numbered by the time they entered the club) has strength Si and beauty Bi. Since this is a very prestigious club, its members are very rich and therefore extraordinary people, so they often extremely hate each other. Strictly speaking, i-th member of the club Mr X hates j-th member of the club Mr Y if Si <= Sj and Bi >= Bj or if Si >= Sj and Bi <= Bj (if both properties of Mr X are greater then corresponding properties of Mr Y, he doesn��t even notice him, on the other hand, if both of his properties are less, he respects Mr Y very much).

To celebrate a new 2005 year, the administration of the club is planning to organize a party. However they are afraid that if two people who hate each other would simultaneouly attend the party, after a drink or two they would start a fight. So no two people who hate each other should be invited. On the other hand, to keep the club prestige at the apropriate level, administration wants to invite as many people as possible.

Being the only one among administration who is not afraid of touching a computer, you are to write a program which would find out whom to invite to the party.


This problem contains multiple test cases!

The first line of a multiple input is an integer N, then a blank line followed by N input blocks. Each input block is in the format indicated in the problem description. There is a blank line between input blocks.

The output format consists of N output blocks. There is a blank line between output blocks.


Input

The first line of the input file contains integer N - the number of members of the club. (2 <= N <= 100 000). Next N lines contain two numbers each - Si and Bi respectively (1 <= Si, Bi <= 109).


Output

On the first line of the output file print the maximum number of the people that can be invited to the party. On the second line output N integers - numbers of members to be invited in arbitrary order. If several solutions exist, output any one.


Sample Input

1

4
1 1
1 2
2 1
2 2


Sample Output

2
1 4


【思路】:其实一开始作为java型选手我想尝试用o(nlogn)的方法在java上实现,可是遗憾的是超时了。。。感觉c++可能更适合于这种大数据的问题。其实这道题不难就是典型的二维递增序列,只要先将s从小大大,b从大到小排序,再用nlogn下LIS算法即可,其实思路不难但是网上对这一类二维递增序列讲的少之又少,所以我想既然遇到了这道题 ,就好好解释一下这道题的思路。

【二维递增序列】:
初始情况:

         i

0

1

2

3

4

         

  P(s,b,indx)

(0,0,0)

(1,2,2)

(1,1,1)

(2,2,4)

(2,1,3)

            




(排序后)

  i     

0     

1             

2          

3             

4            

DP

0       

1      

0            

0           0

 0

Fa        

0       

0           

0         

0    

0               









首先,(1,1)和(1,2)比较,b:1<2,所以而二分查找第一个不比(1,1)大的位置,此时为1;将dp[1]=2;fa[2]=dp[1-1]=0;




  i     

0     

1             

2          

3             

4            

DP

0       

2      

0            

0          

 0

Fa        

0       

0           

0         

0    

0               








然后,(2,2)和(1,1)比较,B:2>1,所以k+1,fa[3]=dp[k],dp[++k]=3,(其实也可以这么写:k=k+1=2;dp【k】=3;fa【3】=dp【k-1】)


  i     

0     

1             

2          

3             

4            

DP

0       

2      

3          

0          

 0

Fa        

0       

0           

0         

2   

0               








最后,(2,1)和(2,2)比较,B:1<2;进行二分查找,确定位置为1;dp[1]=i=4;fa[4]=dp[1-1]=0;

  i     

0     

1             

2          

3             

4            

DP

0       

3           

0          

 0

Fa        

0       

0           

0       

2    

0               








而我们要的答案就是递推dp【k】和fa【i】
dp[k]=dp[2]=4,printf:p[4].index,k--
i=fa[k],若k>0,printf:p【i】.index,k--
此时k=0,输出结束。
【题外话】:
其实nlogn的方法只是在n^2的基础上改遍历为二分而已,而用s从小到大,b从大到小的排序方法,可以确保每一次取到的数据S1都满足S1》=s,这样可以避免还要对s进行判断。
【AC代码】:
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <cmath>
#include <algorithm>
#define N 100010
using namespace std;
struct peo
{
    int s,b,index;
} p[N];
int cmp(peo a, peo b)
{
    if(a.s!=b.s)
        return a.s<b.s;//"<"从小到大,">"从大到小!
    else
        return a.b>b.b;
}
int dp[N];//记录长度为i的最长递增序列的最后一个元素在p中的序号
int fa[N];//记录每一个人在最长递增序列中前面一个元素在p中的序号
int n,m,k;

int main()
{
    scanf("%d",&n);
    while(n--)
    {
        scanf(" %d",&m);
        for(int i=1; i<=m; ++i)
        {
            scanf(" %d %d",&p[i].s,&p[i].b);
            p[i].index=i;
        }
        sort(p+1,p+m+1,cmp);
        k=1;
        dp[k]=1;
        fa[1]=0;
        for(int i=2; i<=m; ++i)
        {
            if(p[i].b>p[dp[k]].b)
            {
                fa[i]=dp[k];
                dp[++k]=i;
            }
            else
            {
                int lf=1,mid,rg=k;
                while(rg>=lf)
                {
                    mid=(lf+rg)>>1;
                    if(p[i].b>p[dp[mid]].b)
                        lf=mid+1;
                    else
                        rg=mid-1;

                }
                dp[lf]=i;
                fa[i]=dp[lf-1];
            }
        }
        printf("%d\n",k);
        int j=dp[k];
        printf("%d",p[j].index);
        while(k-->1)
        {
            j=fa[j];
            printf(" %d",p[j].index);

        }
        printf("\n");



    }


    return 0;
}


【pis】:可以参考一下这篇博文理解二维最长递增子序列
http://www.cppblog.com/mysileng/archive/2012/11/30/195841.html























  i     

0     

1             

2          

3             

4            

DP

0       

1      

0            

0           0

 0

Fa        

0       

0           

0         

0    

0               







  i     

0     

1             

2          

3             

4            

DP

0       

1      

0            

0           0

 0

Fa        

0       

0           

0         

0    

0               







  i     

0     

1             

2          

3             

4            

DP

0       

1      

0            

0           0

 0

Fa        

0       

0           

0         

0    

0               







  i     

0     

1             

2          

3             

4            

DP

0       

2      

3          

0          

 0

Fa        

0       

0           

2         

0    

0               






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值