题目连接:
链接:https://ac.nowcoder.com/acm/problem/201994
来源:牛客网
Description
现在你有 N 块矩形木板,第 i 块木板的尺寸是 (X_i*Y_i),你想用这些木板来玩汉诺塔的游戏。
我们知道玩汉诺塔游戏需要把若干木板按照上小下大的顺序堆叠在一起,但因为木板是矩形,所以有一个问题:
第 i 块木板能放在第 j 块木板上方当且仅当 (X_i<X_j) 且 (Y_i<Y_j),于是你很可能没法把所有的木板按照一定的次序叠放起来。
你想把这些木板分为尽可能少的组,使得每组内的木板都能按照一定的次序叠放。
你需要给出任意一种合理的分组方案。
提醒:“任意”意味着你的答案不必和标准输出完全一致,只要正确即可。
Input
第一行,一个正整数 N
接下来 N 行,每行两个正整数表示 (X_i) 和 (Y_i)
对于所有的数据,(1≤N≤100,000,1≤X_i,Y_i≤N,X_i) 互不相等且 (Y_i) 互不相等
Output
输出文件包含两行,第一行一个正整数,表示最少组数
第二行 N 个正整数,依次表示你的方案中每块木板分在了哪一组
组的编号必须是从 1 开始的连续整数
Sample Input
3
1 1
2 3
3 2
Sample Output
2
1 1 2
题意
求最少上升子序列个数,以及输出分组方案
做法1:贪心优化(Dilworth定理)
题解:
(x降序排的)只保存每个序列的最小一个数字,每次二分查找小于且最接近这个数的最小序列末尾,如果有就加到这个序列末尾,否则就作为一个新的序列。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=100100;
struct node{
int x,y,id;
}arr[N];
int ans[N],f[N];
bool cmp(node a,node b){
return a.x>b.x;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;++i) cin>>arr[i].x>>arr[i].y,arr[i].id=i;
sort(arr+1,arr+1+n,cmp);
int m=0;
for(int i=1;i<=n;++i){
int p=upper_bound(f+1,f+1+m,arr[i].y)-f;
f[p]=arr[i].y;
if(p>m) m=p;
ans[arr[i].id]=p;
}
cout<<m<<endl;
for(int i=1;i<=n;++i) cout<<ans[i]<<" ";
return 0;
}
做法2:树状数组优化
题解:
同样对x降序排序,我们将每个y加到作为下标,值为分组编号插入树状数组(维护区间最大值)时,先查询1~y中分组最大的编号id,这些序列是当前y不能加入的,因为x是降序排的嘛,y需要插到大于他的y后面,那么y插入的编号就是id+1。这样如果之前存在id+1这个分组,y就相当于插到后面了,如果之前不存在id+1,那么y就相当于新开了一个序列。妙
代码
#include<bits/stdc++.h>
using namespace std;
const int N=100100;
struct node{
int x,y,id;
}arr[N];
int tr[N],m,ans[N];
bool cmp(node a,node b){
return a.x>b.x;
}
inline int Lowbit(int x){
return x&(-x);
}
int query(int x){
int res=0;
while(x>0){
res=max(res,tr[x]);
x-=Lowbit(x);
}
return res;
}
void change(int x){
int num=query(x)+1;
m=max(m,num);
while(x<N){
tr[x]=max(tr[x],num);
x+=Lowbit(x);
}
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;++i) cin>>arr[i].x>>arr[i].y,arr[i].id=i;
sort(arr+1,arr+1+n,cmp);
for(int i=1;i<=n;++i){
int y=arr[i].y;
change(y);
ans[arr[i].id]=tr[y];
}
cout<<m<<endl;
for(int i=1;i<=n;++i) cout<<ans[i]<<" ";
return 0;
}