某位大佬说过,算法就是锻炼你的思维,通过下面这道题,确实暴露了我的智商
题目传送门
说一下正确的思路:通过例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;
}