2020算法设计竞赛 C 汉诺塔
作者:珩月
链接:https://ac.nowcoder.com/discuss/367149
来源:牛客网
将木板按照Xi从小到大排序,将这时的Yi数列记为Zi数列,则问题变成将Zi划分为尽可能少的若干组上升子序列。
根据Dilworth定理,最小组数等于Zi的最长下降子序列长度。
要求最长下降子序列的长度,我们有一种经典的二分优化dp的方法,在这里不再详述。 借助这种做法我们能给出一种构造方法,在求出最小组数的同时得出方案。
将状态数组的每个位置变为栈,用入栈操作代替修改元素操作,即可在求出组数的同时,用这些栈来完成对数列的划分。
在用这种方法的时候,我们假定x序列已经从小到大排,然后y序列为:6 3 2 5 4 7 1
那么得出的序列划分就为:1 2 3 2 3 1 4
我们将6作为第一个区间中的数,3比他小,所谓dp值为2,作为第二个区间,2比3还小,所以第三个区间,而5比6小,所以第二个区间
这里肯定能满足,已经在第二区间的数肯定小于刚加入区间的数,因为如果比他大的话,那么就长度+1,不会加在这个区间了。
然后上文:根据Dilworth定理,最小组数等于Zi的最长下降子序列长度。
代码如下:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 7 using namespace std; 8 const int N = 100050; 9 10 struct node{ 11 int x, y, loc; 12 inline bool operator < (const node &b)const{ 13 return x < b.x; 14 } 15 }a[N]; 16 int n, m, ot[N], S[N]; 17 18 int main() 19 { 20 21 int i, j, k; 22 scanf("%d", &n); 23 for(i = 1; i <= n; i ++){ 24 scanf("%d%d", &a[i].x, &a[i].y); 25 a[i].loc = i; 26 } 27 sort(a + 1, a + n + 1); 28 for(i = 1; i <= n; i ++){ 29 int lb = 1, rb = m; 30 while(lb <= rb){ 31 int md = lb + rb >> 1; 32 if(S[md] < a[i].y) 33 rb = md - 1; 34 else 35 lb = md + 1; 36 } 37 S[lb] = a[i].y; 38 if(lb > m) m = lb; 39 ot[a[i].loc] = lb; 40 } 41 printf("%d\n", m); 42 for(i = 1; i <= n; i ++) 43 printf("%d ", ot[i]); 44 45 return 0; 46 }