ACdream 1216 Beautiful People

        题意: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;
}



你可能感兴趣的:(最长公共子序列,ACdream)