洛谷题单指南-前缀和差分与离散化-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;
}