问题描述:
小明买了一些玩具士兵,他邀请小红一起玩。他总共有n个士兵,刚开始时,这n个士兵被排成一列,第i个士兵的战斗力为hi。然后小明和小红开始给它们排序。二人总共进行了m次操作。小明的每次操作会选择一个数k,将前k个士兵按战斗力从小到大排序。小红的每次操作会选择一个数k,将前k个士兵按战斗力从大到小排序。请问所有操作结束后从前往后每个士兵的战斗力是多少?
思路:
由于没有说这些操作是交替进行的,想到了单调栈的思路,可以压缩执行的数量。
容易观测出:
- 后一个操作如果有更大的 k k k值肯定会掩盖掉前一个操作
- 而且如果连续操作的类型相同(即连续若干操作的 t t t相同),即使是比当前栈顶元素的 k k k小也不入栈。
如果保证好这两个条件,那么最终栈中的元素,对 k k k而言,肯定是单调递减的,而且类型是交错的。即若前一个是 t = 1 t = 1 t=1,那么下一个肯定是 t = 2 t = 2 t=2。
因此可以进一步优化:只需进行一次排序即可
即只需按照最大的
k
k
k值进行一遍排序。
对栈中的元素两个两个进行比较,分别记为
t
1
,
k
1
,
t
2
,
k
2
&
&
k
1
>
k
2
t_1,k_1,t_2,k_2\quad \&\&\quad k_1 > k_2
t1,k1,t2,k2&&k1>k2,若
t
1
=
=
1
t_1 == 1
t1==1,那么从
[
k
2
,
k
1
−
1
]
[k_2 , k_1 - 1]
[k2,k1−1]之间的元素肯定是前k_1中最大的
k
1
−
k
2
k_1 - k_2
k1−k2个;同理,若
t
1
=
=
2
t_1 == 2
t1==2,那么从
[
k
2
,
k
1
−
1
]
[k_2 , k_1 - 1]
[k2,k1−1]肯定是最小的
k
1
−
k
2
k_1 - k_2
k1−k2个。可以采用双指针的做法,初始时,cur_min指向0,cur_max指向
k
m
a
x
−
1
k_{max} - 1
kmax−1。进行维护即可
代码:
#include <iostream>
#include <vector>
#include <algorithm>
#include <deque>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
vector<long long> arr(n);
vector<long long> arr1(n);
for(int i = 0; i < n; i++)
{
cin >> arr[i];
arr1[i] = arr[i];
}
int t, k;
deque<pair<int, int>> s;
for(int i = 0; i < m; i++)
{
cin >> t >> k;
while( !s.empty() && s.back().second <= k )
{
s.pop_back();
}
if( !s.empty() && s.back().first == t && s.back().second > k ) // 最后堆栈中的应该为递减序列,并且是交替进行
{
continue;
}
s.push_back({t, k});
}
int maxk = s.front().second;
sort(arr1.begin(), arr1.begin() + maxk);
int cur_min = 0;
int cur_max = maxk - 1;
int t1, k1, t2, k2;
t1 = s.front().first;
k1 = s.front().second;
s.pop_front();
while( !s.empty())
{
t2 = s.front().first;
k2 = s.front().second;
s.pop_front();
if(t1 == 1) {
for(int j = k1 - 1; j >= k2; j--)
{
arr[j] = arr1[cur_max];
cur_max--;
}
} else {
for(int j = k1 - 1; j >= k2; j--)
{
arr[j] = arr1[cur_min];
cur_min++;
}
}
t1 = t2;
k1 = k2;
}
if(t1 == 1) {
for(int j = k1 - 1; j >= 0; j--)
{
arr[j] = arr1[cur_max];
cur_max--;
}
} else {
for(int j = k1 - 1; j >= 0; j--)
{
arr[j] = arr1[cur_min];
cur_min++;
}
}
for(int i = 0; i < n; i++)
{
cout << " " << arr[i];
}
cout << endl;
return 0;
}