2020牛客跨年賽部分題解
阿新 • • 發佈:2021-01-27
技術標籤:演算法
某位大佬說過,演算法就是鍛鍊你的思維,通過下面這道題,確實暴露了我的智商
題目傳送門
說一下正確的思路:通過例1發現,只要找到初始位置以及反轉之後的位置,然後進行累加距離差就行。然而例2告訴我們另一種情況(如果沒有給你說明例2,你會想到這種情況嘛),即一個數字出現多次。我們發現,最小花費其實就是距離差最小,所以當一個數字出現多次時,我們讓原序列中排在前面的那個數字和反轉序列中的最前的那個數字,這樣就保證了距離差最小(比如有三個1,位置設為x2,x4,x6,反轉之後序列為x9,x7,x5,這樣我們肯定讓x2與反轉之後的x5進行操作,x4與x7)。更詳細的在程式碼裡面
#include <bits/stdc++.h>
using namespace std;
deque<int> d[100010];// 雙端佇列
int z[100010]; // 用來儲存初始序列
int main()
{
int n;
int x;
long long ans=0; // 不要忘了 long long,爆了一次
cin>>n;
for(int i=1;i<=n;i++)
cin>>z[i];
for(int i=1;i<=n;i++)
{
/* z[i]表示的是序列中i位置的那個數字
d[z[i]] 表示的相同數字出現的位置的集合,n-i+1,表示反轉之後的位置
這裡是加入到隊首,因為反轉之後的位置越來越小,加入隊首保證了隊首
是最小的位置,下面舉例
1 1 2 3 1 a[i] 下標從1開始
1 3 2 1 1 反轉之後
那麼d[a[1]]=d[1]={5} 第一步
d[a[1]]=d[1]={2,5} 加入隊首,這樣就保證
d[a[1]]=d[1]={1,2,5};
*/
d[z[i]].push_front(n-i+1);
}
for(int i=1;i<=n;i++)
{
/*
對於原序列的每一個數字,找出他反轉之後的位置集合,找出隊首那個,
因為隊首那個最小,距離差最小
*/
x=d[z[i]].front();
d[z[i]].pop_front(); // 最小的出列
ans+=abs(i-x);
}
cout<<ans/2<<endl; // 掃描原序列時每一個位置都算了兩遍,所以除2
return 0;
}