题意:n个人,每个人有两个属性S和B,每两个人之间,如果不是一个人两个属性都严格高于另一个人,他们就会互相hate。求最多能有多少人在一起,互相不会hate。
思路:最长公共子序列。我们可以这样考虑,按S升序排序,S相同的人分为一档,每档里面最多只能选一个人,否则就会互相hate。如果B也升序排序,问题就变成了每段只能选一个的最长公共子序列。但是,如果B降序排序呢,这样排序之后再求LIS就自动保证了S各不相同,问题也就转换成了普通的LIS。还有就是需要开辅助的数组记录选择,算法得用O(nlogn)的,否则超时。
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
#define INF 1000000010
struct mem{
int s;
int b;
int index;
};
mem M[100010];
int dp[100010];
int dp2[100010];
int last[100010];
int choose[100010];
bool cmp(mem a,mem b){
if(a.s==b.s)return a.b>b.b;
return a.s<b.s;
}
int main(){
int n;
while(cin>>n){
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
scanf("%d%d",&M[i].s,&M[i].b);
M[i].index=i;
dp2[i]=INF;
}
sort(M+1,M+n+1,cmp);
int ans=0;
int k;
for(int i=1;i<=n;i++){
int find=-1;
int l=1;int r=ans+1;//二分查找
while(l!=r){
int mid=(l+r)/2;
if(M[i].b<dp2[mid]){
if(M[i].b>dp2[mid-1]){
find=mid;break;
}else{
r=mid;
}
}else{
if(l!=mid)l=mid;
else l++;
}
}
if(M[i].b>dp2[l-1]&&M[i].b<dp2[l])find=l;
if(find==-1)continue;
if(find>=dp[i]){
dp2[find]=M[i].b;
dp[i]=find;
choose[find]=i;
last[i]=choose[find-1];
if(find>ans){
ans=find;
k=i;
}
}
}
cout<<ans<<endl;
while(k){
printf("%d ",M[k].index);
k=last[k];
}
printf("\n");
}
return 0;
}