题目描述
现在你有 N 块矩形木板,第 i 块木板的尺寸是 Xi*Yi,你想用这些木板来玩汉诺塔的游戏。
我们知道玩汉诺塔游戏需要把若干木板按照上小下大的顺序堆叠在一起,但因为木板是矩形,所以有一个问题:
第 i 块木板能放在第 j 块木板上方当且仅当 Xi<Xj 且 Yi<Yj,于是你很可能没法把所有的木板按照一定的次序叠放起来。
你想把这些木板分为尽可能少的组,使得每组内的木板都能按照一定的次序叠放。
你需要给出任意一种合理的分组方案。
提醒:“任意”意味着你的答案不必和标准输出完全一致,只要正确即可。
输入描述
第一行,一个正整数 N
接下来 N 行,每行两个正整数表示 Xi 和 Yi
对于所有的数据,1≤N≤100,000,1≤Xi,Yi≤N,Xi 互不相等且 Yi 互不相等
输出描述
输出文件包含两行,第一行一个正整数,表示最少组数
第二行 N 个正整数,依次表示你的方案中每块木板分在了哪一组
组的编号必须是从 1 开始的连续整数
示例输入
3
1 1
2 3
3 2
示例输出
2
1 1 2
分析:
由于Xi互不相等,那么我们先根据X降序排列,考虑这时的Yi序列,则问题变成将Yi划分为尽可能少的若干组下降子序列。
根据Dilworth定理,下降子序列的最小组数等于最长上升子序列。
要求最长上升子序列的长度并且构造各组下降子序列,这里采用二分优化的DP。对于它的基础理解可以参考这篇博客。
用 dp[i] 表示长度为 i 的最长上升子序列的最小末元素,那么其实更新dp数组同一位置的一组数就能够构成一组下降子序列,所以我们能在确定最长上升子序列的长度的同时构造出所有的下降子序列。
具体解释见代码。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <queue>
#define INF 0x3f3f3f3f
#define mst(a,num) memset(a,num,sizeof a)
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
const ll mod = 1e9 + 7;
const int maxn = 100000 + 5;
struct node
{
int x,y,id;
bool operator< (const node&a)const{
return x>a.x;
}
}p[maxn];
int dp[maxn],ans[maxn];
int main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&p[i].x,&p[i].y);
p[i].id=i;
}
sort(p+1,p+n+1); //按照Xi从大到小排序
int len=1;
dp[1]=p[1].y; //初始化dp[1]
ans[p[1].id]=1; //放入第1组
for(int i=2;i<=n;i++){
int l=1,r=len,res;
if(dp[len]<p[i].y){ //自成一组
len++;
dp[len]=p[i].y;
ans[p[i].id]=len;
continue;
}
while(l<=r){ //二分查找dp数组中比p[i].y大的最小数
int mid=(l+r)/2;
if(dp[mid]>=p[i].y) r=mid-1,res=mid;
else l=mid+1;
}
dp[res]=p[i].y; //更新dp数组
ans[p[i].id]=res; //放入第res组
}
printf("%d\n",len);
for(int i=1;i<=n;i++) printf("%d%c",ans[i],i==n?'\n':' ');
return 0;
}