洛谷題單指南-字首和差分與離散化-P4375 [USACO18OPEN] Out of Sorts G

五月江城發表於2024-08-07

原題連結: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;
}

相關文章