Codeforces:"North-East"
题目链接:http://codeforces.com/gym/101246/problem/H
题目大意:空间内有$n$个点,现取$x$和$y$严格递增的点组成最长序列,问可能取到哪些点,一定取到哪些点.
DP
这道题要求的是二维LIS,可以按$x$递增排序将其降为一维,当横坐标相等时,因为要求坐标严格递增,按$y$递减排序。
从后面往前搞,$maxy[len]$记录LIS长度为$len$的点的最大$y$,若以当前点为结尾的LIS长度为$len$,当前点的$y$小于$maxy[len+1]$则为可能取到的点。
代码如下:
1 #include <cstdio> 2 #include <algorithm> 3 #include <vector> 4 #include <set> 5 #define N 100005 6 using namespace std; 7 struct node{ 8 int x,y,id; 9 friend bool operator<(node a,node b){ 10 if(a.x==b.x)return a.y>b.y; 11 return a.x<b.x; 12 } 13 }a[N]; 14 const int inf=1000005; 15 int n,dp[N],val[N],k,pos[N],maxy[N]; 16 vector<int>v[N]; 17 set<int>A,B; 18 void init(){ 19 freopen("input.txt","r",stdin); 20 freopen("output.txt","w",stdout); 21 } 22 bool cmp(int x,int y){ 23 return val[x]>val[y]; 24 } 25 void output(){ 26 printf("%d",A.size()); 27 for(set<int>::iterator it=A.begin();it!=A.end();++it) 28 printf(" %d",*it); 29 printf(" "); 30 printf("%d",B.size()); 31 for(set<int>::iterator it=B.begin();it!=B.end();++it) 32 printf(" %d",*it); 33 printf(" "); 34 } 35 int main(void){ 36 init(); 37 scanf("%d",&n); 38 for(int i=0;i<n;++i){ 39 scanf("%d%d",&a[i].x,&a[i].y); 40 a[i].id=i+1; 41 } 42 sort(a,a+n); 43 for(int i=0;i<n;++i){ 44 int v=a[i].y; 45 int t=lower_bound(dp,dp+k,v)-dp; 46 dp[t]=v; 47 val[a[i].id]=a[i].y; 48 pos[a[i].id]=t; 49 if(t==k)k++; 50 } 51 for(int i=0;i<k;++i) 52 maxy[i]=-inf; 53 maxy[k]=inf; 54 for(int i=n-1;i>=0;--i){ 55 int id=a[i].id; 56 int p=pos[id]; 57 if(val[id]<maxy[p+1]){ 58 A.insert(id); 59 maxy[p]=max(maxy[p],val[id]); 60 v[p].push_back(id); 61 } 62 } 63 for(int i=0;i<k;++i) 64 if((int)v[i].size()==1) 65 B.insert(v[i][0]); 66 output(); 67 }