原題連結
解析
在操作一時,最小值如果在最後一位,其無法更新任何數,會被刪除;否則不在最後一位時一定會被其右側更大的數更新。所以在操作一時,最小值一定會被更新掉。
同理,在操作二時,最大值一定會被更新掉。
由此,操作一決定了答案的下限,操作二決定了答案的上限。
所以可以得出貪心策略:先進行 \(m\) 次操作一,後進行 \(n-m-1\) 次操作二。
進行完操作一以後,很明顯如果只進行操作二,那麼最後剩下的一定是剩下數的最小值。
\(m\) 次操作一的時間複雜度為 \(O(NM)\),可以得 \(40\) 分,但操作一的過程還可以最佳化。
因為 \(m\) 次操作一可以使 \(a_i\) 影響到 \(a_{i-1},a_{i-2},\cdots,a_{i-m}\),所以反過來,\(a_i\) 在經過 \(m\) 次操作一以後被 \(a_{i+1},a_{i+2},\cdots,a_{i+m}\) 影響,\(a_i\) 即為 \(\max\{a_j\}\),其中 \(j \in [i+1,i+m]\)。
問題轉化成了“對於每個數,求它右邊 \(m\) 個數的最大值”,可以用優先佇列維護。
程式碼:
40 Pts 程式碼
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+5;
int n,m,a[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n-i;j++)
a[j]=max(a[j],a[j+1]);
}
int ans=0x3f3f3f3f;
for(int i=1;i<=n-m;i++)
ans=min(ans,a[i]);
printf("%d\n",ans);
return 0;
}
100Pts 程式碼
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e6+5;
int n,m,a[N],b[N];
deque<int> q;
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
if(!q.empty() && i-q.front()>m) q.pop_front();
while(!q.empty() && a[q.back()]<a[i]) q.pop_back();
q.push_back(i);
if(i>=m+1) b[i-m]=a[q.front()];
}
int ans=0x3f3f3f3f;
for(int i=1;i<=n-m;i++)
ans=min(ans,b[i]);
printf("%d\n",ans);
return 0;
}