2020牛客寒假算法基础集训营6.C——汉诺塔【贪心 & Dilworth定理 & 二分】【求上升子序列最小化分数】

题目传送门

题目描述

现在你有 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


题解

  • 首先按照 x i x_i 从小到达排序,则问题转化为 y i y_i 全序集里求上升子序列最小划分组数
  • 由Dilworth定理: 对于任意有限偏序集,其最大反链中元素的数目必等于最小链划分中链的数目
  • 那么我们就可以求 y i y_i 序列的最长下降子序列长度(由于题目已知不存在相等元素,否则应该为不上升子序列长度)
  • 然后就变成了板子题了,细节见代码注释

AC-Code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;

struct Node {
	int x, y, id;
	bool operator < (const Node& t)const {
		return x < t.x;
	}
}a[maxn];
int ans[maxn]; // 组号
int st[maxn]; // 记录长度为i的下降子序列末尾元素(相同长度记录大的)
int main() {
	int n;	while (cin >> n) {
		for (int i = 1; i <= n; ++i) {
			cin >> a[i].x >> a[i].y;
			a[i].id = i;
		}
		sort(a + 1, a + 1 + n); // 按照x升序排序
		int m = 0; // 记录最长下降子序列长度(Dilworth定理:=上升子序列最小划分数)
		for (int i = 1; i <= n; ++i) {
			int l = 1, r = m; // 二分查找范围
			while (l <= r) {
				int mid = (l + r) >> 1; // 题目没有相等情况
				if (a[i].y > st[mid])		r = mid - 1; 
				else		l = mid + 1;
			}
			st[l] = a[i].y;
			if (l > m)	m = l; // 更新最长下降子序列长度
			ans[a[i].id] = l;
		}
		cout << m << endl;
		for (int i = 1; i <= n; ++i)	cout << ans[i] << " ";
	}
}
发布了197 篇原创文章 · 获赞 125 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Q_1849805767/article/details/104395414