洛谷 P4375 [USACO18OPEN]Out of Sorts G(树状数组求冒泡排序循环次数加强版)

传送门

 

参考资料:

  [1]:https://www.cnblogs.com/Miracevin/p/9662350.html

  [2]:https://blog.csdn.net/lengxuenong/article/details/80482202?utm_source=blogxgwz1

  今天已经理解了一晚上了,还是处于懵懵懂懂的状态,明天在肝一天,一定要将自己理解的题解写出来,哇咔咔!

 

题解

  定义数组 a;

  先将含有 n 个元素的数组 a 离散化,则离散化后的 n 个元素对应 1~n 的某个排列,而我们所要求的就是将离散化后的排列变为 1,2,3,...,n 所需的循环次数

  对于位置 i ,在这个双向排序过程中,每次 while( ) 循环会把一个 i 之前的大于 i 的数移到 i 后面,并且把一个 i 之后小于 i 的数移到 i 的前面

  for : i  1 to N

    我们可以对于所有的位置 i ,找到[1,i]范围内比 i 小的数的个数 s,i-s 就是 i 位置把小于 i 的放在 i 位置前面,大于 i 的放在 i 位置后面的循环次数。

对红色字体的理解

  冒泡排序,通过交换相邻两数最终使得数组有序;

  对于每个位置 i ,如果比 i 大的数都交换到 i 位置之后,比 i 小的数都交换到 i 位置之前;

  那么,等结束最后一个 i 位置的时候,冒泡排序也就结束了,此时数组也就是有序的。

  因此,对于此题的双向循环,每次判断当前位置 i 需要通过多少次while( )循环才能使得:

  比 i 大的数都交换到 i 位置之后,比 i 小的数都交换到 i 位置之前;

  因此,只需要求出 [1,i] 位置上比 i 大的数的个数,输出最大的那个即是答案;

•Code

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define lowbit(x) (x&(-x))
 4 const int maxn=1e5+50;
 5 
 6 int n;
 7 struct Node
 8 {
 9     int val;
10     int id;
11     int newVal;
12 }a[maxn];
13 //==================BIT=====================
14 int bit[maxn];
15 void Add(int x)
16 {
17     while(x <= n)
18     {
19         bit[x]++;
20         x += lowbit(x);
21     }
22 }
23 int Sum(int x)
24 {
25     int sum=0;
26     while(x > 0)
27     {
28         sum += bit[x];
29         x -= lowbit(x);
30     }
31     return sum;
32 }
33 //=======================================
34 bool cmp(Node _a,Node _b) {//注意,如果val相同,初始编号 id 小的在前
35     return _a.val < _b.val || (_a.val == _b.val && _a.id < _b.id);
36 }
37 bool cmp1(Node _a,Node _b){//将数组恢复到刚开始输入时的状态
38     return _a.id < _b.id;
39 }
40 void Solve()
41 {
42     sort(a+1,a+n+1,cmp);//离散化
43     for(int i=1;i <= n;++i)
44         a[i].newVal=i;//存储离散化后的值
45     sort(a+1,a+n+1,cmp1);
46     int res=1;
47     for(int i=1;i <= n;++i)
48     {
49         Add(a[i].newVal);
50         res=max(res,i-Sum(i));//询问每个位置,找到所需的最大的循环次数
51     }
52     printf("%d\n",res);
53 }
54 int main()
55 {
56     scanf("%d",&n);
57     for(int i=1;i <= n;++i)
58         scanf("%d",&a[i].val),a[i].id=i;
59     Solve();
60 }
View Code

•初始疑惑

  将 i 之前的大于 i 的数归位,貌似只用到了问题中的第一个for( ),而并没有用第二个 for( )循环啊;

  那为什么 i-s 就是 i 位置把小于 i 的放在 i 位置前面,大于 i 的放在 i 位置后面的循环次数呢?

我的理解:

  ①如果将 [1,i] 位置中的大于 i 的数全部移到 i 位置后,[i+1,n]位置中的数肯定全部大于 i;

  如果 [i+1,n] 位置存在小于 i 的数,那,[1,i]中肯定存在大于 i 的数,不然,怎么交换呢?

  这样的话,[1,i]位置中大于 i 的数就没有全部移到 i 位置后,与条件①矛盾;

  所以条件①成立;

 

 


分割线:2019.7.17

•感想

  当初理解了好长时间的东西到如今成了自然而然地事;

•Code

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define lowbit(x) (x&-x)
 4 const int maxn=1e5+50;
 5 
 6 int n;
 7 struct Data
 8 {
 9     int v;
10     int id;
11     int newV;
12 }data[maxn];
13 struct BIT
14 {
15     int bit[maxn];
16     void Init()
17     {
18         memset(bit,0,sizeof(bit));
19     }
20     void add(int t)
21     {
22         while(t < maxn)
23         {
24             bit[t]++;
25             t += lowbit(t);
26         }
27     }
28     int Sum(int t)
29     {
30         int sum=0;
31         while(t > 0)
32         {
33             sum += bit[t];
34             t -= lowbit(t);
35         }
36         return sum;
37     }
38 }_bit;
39 
40 ///树状数组求逆序对时,v相同的一定要让编号小的在前
41 bool cmp1(Data a,Data b)
42 {
43     if(a.v != b.v)
44         return a.v < b.v;
45     return a.id < b.id;
46 }
47 bool cmp2(Data a,Data b)
48 {
49     return a.id < b.id;
50 }
51 int Solve()
52 {
53     sort(data+1,data+n+1,cmp1);
54     for(int i=1;i <= n;++i)
55         data[i].newV=i;
56     sort(data+1,data+n+1,cmp2);
57 
58     _bit.Init();
59     int ans=1;
60     for(int i=1;i <= n;++i)
61     {
62         _bit.add(data[i].newV);
63         ans=max(ans,i-_bit.Sum(i));
64     }
65 
66     return ans;
67 }
68 int main()
69 {
70 //    freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
71     scanf("%d",&n);
72     for(int i=1;i <= n;++i)
73     {
74         scanf("%d",&data[i].v);
75         data[i].id=i;
76     }
77     printf("%d\n",Solve());
78 
79     return 0;
80 }
View Code

 

posted @ 2018-10-22 22:13  HHHyacinth  阅读(388)  评论(2编辑  收藏  举报