ACM/ICPC 之 数据结构-线段树思想(POJ2182,含O(n^2)插入式解法)

这道题在一定程度上体现了线段树的一种用法,解决的问题是:对于总计n个元素的第i个元素,已知其在[1,i]上部分序列的排名,求第i个元素在所有n个元素中的排名。

当然这道题数据比较水,所以用O(n^2)的直接解法也可以解出,在这里,我也给出自己的O(n^2)解法。

 

 

  题目大意:

    n头乱序的牛排列在一行,每头牛都有一个牌号(1-n),现在知道所有牛此前有多少头牛的牌号比该牛的牌号要小,求每头牛的牌号。

 

  直接解法(插入式):

    从前向后遍历每一个数据,每次都进行一次插入。

    具体来说:例如对于(1,0,1)这样的序列,我们先设brand[1] = 1。

      由第一个数据(1)有brand[2] = 2    ————有排名为:(1,2)

      由第二个数据(0)有brand[3] = 1,此时遍历此前所有元素,查询到>=brand[3]则加一个排名,得到brand[1] = 2,brand[2] = 3;  ——排名(2,3,1)

      由第三个数据(1)有brand[4] = 2,此时遍历此前元素,参照上面的方法,得到brand[1] = 3,brand[2] = 4;  ——(3,4,1,2)

    那么最后能够得到排名(3,4,1,2);

 

  Code如下:

《ACM/ICPC 之 数据结构-线段树思想(POJ2182,含O(n^2)插入式解法)》
《ACM/ICPC 之 数据结构-线段树思想(POJ2182,含O(n^2)插入式解法)》

 1 //普通的O(n^2)算法-直接的想法
 2 //n头牛,已知第i头牛在1-i部分序列的排名,求所有牛的牌号(排名)
 3 //Memory: 236K Time:375Ms
 4 #include<iostream>
 5 #include<cstdio>
 6 #include<cstring>
 7 using namespace std;
 8 #define MAX 8005
 9 int n;    //cow头量
10 int small[MAX];    //比第i头cow靠前但牌号较小的cow头量
11 int brand[MAX];    //牌号
12 int main()
13 {
14     scanf("%d", &n);
15     brand[1] = 1;
16     for (int i = 2; i <= n; i++)
17     {
18         scanf("%d", &small[i]);
19         brand[i] = small[i] + 1;
20         if (small[i] + 1 != i)    //不是排在最后
21         {
22             for (int j = 1; j < i; j++)
23             {
24                 if (brand[j] >= brand[i])    //>=该序号则+1
25                     brand[j]++;
26             }
27         }
28     }
29     for (int i = 1; i <= n; i++)
30         printf("%d\n", brand[i]);
31     return 0;
32 }

插入式-解法

 

 

  线段树解法:

    大体想法就是:用线段树存储一个从1-n的顺序排名,将数据从后向前遍历,每次遍历到某一个位置时,假设原数据为s[],那么对于s[i],其在1-i的排名就是s[i]+1,那么在线段树中将[s[i]+1,s[i]+1]区间的sum置为0(减去1)。也就是删去s[i]+1这个排名,这样排名的整体规模就缩小了一个单位,即原为n个元素排名,现在是n-1个元素排名(第s[i]+1排名被删去),并保留原排名数值不变。

    这样不断遍历,不断缩减排名的规模就可以依次反向得到该乱序的牛的牌号,然后顺序输出牌号即可

  Code如下:

  

《ACM/ICPC 之 数据结构-线段树思想(POJ2182,含O(n^2)插入式解法)》
《ACM/ICPC 之 数据结构-线段树思想(POJ2182,含O(n^2)插入式解法)》

 1 //线段树
 2 //n头牛,已知第i头牛在1-i部分序列的排名,求所有牛的牌号(排名)
 3 //Memory: 432K Time:47Ms
 4 #include<iostream>
 5 #include<cstdio>
 6 #include<cstring>
 7 using namespace std;
 8 #define MAX 8005
 9 int n;    //cow头量
10 int small[MAX];    //比第i头cow靠前但牌号较小的cow头量
11 int brand[MAX];    //牌号
12 //Brand interval数组
13 struct Brands{
14     int l, r;    //牌号区间
15     int sum;    //头数
16 }tree[4*MAX];
17 /*从x开始搭建interval tree*/
18 void build(int x,int l,int r)
19 {
20     tree[x].l = l;
21     tree[x].r = r;
22     if (l == r){
23         tree[x].sum = 1;
24         return;
25     }
26     int mid = (l + r) / 2;
27     build(2 * x, l, mid);        //left son
28     build(2 * x + 1, mid + 1, r);    //right son
29     tree[x].sum = tree[2 * x].sum + tree[2 * x + 1].sum;
30 }
31 int query(int x, int v)
32 {
33     tree[x].sum--;
34     // Hit!
35     if (tree[x].l == tree[x].r)
36         return tree[x].l;
37     if (v <= tree[2 * x].sum)    //<=左结点未删减序列数量
38         query(2 * x, v);
39     else query(2 * x + 1, v - tree[2 * x].sum);
40 }
41 int main()
42 {
43     scanf("%d", &n);
44     for (int i = 2; i <= n; i++)
45         scanf("%d", &small[i]);
46     small[1] = 0;
47     build(1, 1, n);
48     for (int i = n; i >= 1; i--)
49         brand[i] = query(1, small[i] + 1);    //牌号
50     for (int i = 1; i <= n; i++)
51         printf("%d\n", brand[i]);
52     return 0;
53 }

线段树-解法

 

 

    原文作者:Inkblots
    原文地址: https://www.cnblogs.com/Inkblots/p/4921890.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞