洛谷题单指南-前缀和差分与离散化-P4375 [USACO18OPEN] Out of Sorts G

原题链接:https://www.luogu.com.cn/problem/P4375

题意解读:计算双向冒泡排序一共要进行多少趟。

解题思路:一道思维难度较大的题!

由于数据各不相同,先将其离散化处理从1~n的数,如果每个数不在自己的位置则是无序。

对于双向冒泡排序,对于第x个位置来说,每一趟正向一定会有一个大于x的数交换到x的后,每一趟反向会有一个小于x的数交换到x前面

因此一共要进行多少趟上述过程?取决于对于每一个位置x,从1~x有多少个数大于x,取最大的个数即可。

这里的关键在于如何统计每个位置前有多少个数大于x!

一种O(n)方法:

设cnt表示截止当前位置有多少个数大于当前位置编号

枚举1~n的位置x

  对于每一个位置的数num[x],如果num[x]>x,则cnt++

  如果x在前面位置作为数值访问过,则当时肯定导致了cnt++,此时必须cnt--,因为此时位置变成x,前面的数值x不能大于x了

  标记num[x]已访问过

  更新答案ans = max(ans, cnt)

注意:如果已经排好序,至少要进行一趟,ans初始值为1。

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
int n;
bool vis[N];
struct node
{
    int val, id;
} a[N];

bool cmp1(node a, node b)
{
    return a.val < b.val;
}

bool cmp2(node a, node b)
{
    return a.id < b.id;
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i].val, a[i].id = i;
    sort(a + 1, a + n + 1, cmp1);
    for(int i = 1; i <= n; i++) a[i].val = i; //离散化
    sort(a + 1, a + n + 1, cmp2); //恢复原顺序

    int ans = 1;
    int cnt = 0; //当前位置i~1有多个数大于i
    for(int i = 1; i <= n; i++)
    {
        if(a[i].val > i) cnt++;
        if(vis[i]) cnt--; //i在前面数字出现过,说明当时肯定导致cnt++,到第i个的时候之前出现的数字i效果就不再,就要cnt--
        vis[a[i].val] = true;
        ans = max(ans, cnt);
    }
    cout << ans;
    return 0;
}

 

posted @ 2024-08-07 10:43  五月江城  阅读(12)  评论(0编辑  收藏  举报