ACM: 强化训练-Beautiful People-最长递增子序列变形-DP

199. Beautiful People

time limit per test: 0.25 sec. 
memory limit per test: 65536 KB
input: standard 
output: standard




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 S i and beauty B i . 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 S i ≤ S j and B i ≥ B j or if S i ≥ S j and B i ≤ B j (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 2003 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 presti≥ 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. 

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 — S i and B i respectively ( 1 ≤ S i, B i ≤ 10 9 ). 

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 test(s)

Input


1 1 
1 2 
2 1 
2 2 
 

Output
 

1 4 
 
/*
最长上升子序列的变形。

要保证Si和Bi都有严格的递增,再求最长上升子序列的元素排序。(这个S和B给满分。。) 

首先想到的是贪心,以两点都升序排序,排序后从最小的往后加,但是这样忽略了其他的更优解WA的理所当然。。 

再后来想了下这题是个背包问题,用dp来记录以这个点结尾的前面的子序列长度,实现过程附图:



以此往下类推


最后得到了一组ans以各个元素结尾的最大的子序列长度

最后这样找出来的顺序就是整个过程最小子序列的顺序,当然一开始id被打乱了,不要忘了把id最后对应一下。 最后还有一个问题就是元素顺序的打印,这里一开始写的递归总有点问题,最后看了下别人的题解借鉴了点用ans标记顺序的思想。
*/
 
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define memset(x,y) memset(x,y,sizeof(x))
using namespace std;
#define MX 100005

int dp[MX],ans[MX];
int Prt[MX]; 
struct node {
	int s,b;
	int id;				//标记实际的输入的顺序 
} rel[MX];

int n,len;

int cmp(node a,node b){
	if(a.s==b.s)
		return a.b>b.b;
	return a.s<b.s;
}

int Query(int len,int x){
	int l,r,mid,rt;
	l=0;r=len;		//用二分的方法往前找出到达这个点的前面的最大单调上升序列的最小的值 
	while(r>=l){
		mid=(l+r)>>1;
		if(dp[mid]<x){
			rt=mid+1;	//找到比x小的数的后一个位置 
			l=mid+1;
		}
		else r=mid-1;
	}	
	return rt;
}

int Print(){
	int j=0;
	 for(int i=n;i>=1;i--){
		if(ans[i]==len) {
		    Prt[j++]=rel[i].id; //保存每个数字最小的结果 
	    	len--;
		} 
	}
	return j;//返回长度 
}

int main(){
	while(~scanf("%d",&n)){
	    for(int i=1;i<=n;i++){
			scanf("%d%d",&rel[i].s,&rel[i].b);
			rel[i].id=i;
		}
		memset(ans,0);
		memset(dp,0);
		sort(rel+1,rel+n+1,cmp);   //将得到的所有值按照 s从小到大,b的从大到小排序 
		len=1;
		dp[1]=rel[1].b; //dp保存最各个长度的最长单调序列的最大的值 
		ans[1]=1;
		int Q;
		for(int i=2;i<=n;i++){
			Q=Query(len,rel[i].b);		//找到比rel[i].b小的最长单调子序列的下一个位置 
			ans[i]=Q;				//ans[i]保存以这点为结尾的最长上升子序列的最大长度						
			dp[Q]=rel[i].b;				//更新这个位置的最小值 
			len=len>Q?len:Q;			//保存最大长度。 
		} 
		int flag=1;
		printf("%d
",len);
		int lon=Print();
		for(int i=lon-1;i>=0;i--){
			if(flag)flag=0;
			else printf(" ");
			printf("%d",Prt[i]); //逆序输出。 
		}
	puts("");
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/HDMaxfun/p/5719692.html