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 }

 

 
posted @ 2020-02-18 10:50  古比  阅读(212)  评论(0编辑  收藏  举报