算法分析
最小化
∑
(
a
i
−
b
i
)
2
=
∑
(
a
i
2
+
2
a
i
b
i
+
b
i
2
)
\sum (a_i-b_i)^2=\sum (a_i^2+2a_ib_i+b_i^2)
∑(ai−bi)2=∑(ai2+2aibi+bi2)
也就是最小化
∑
a
i
b
i
\sum a_ib_i
∑aibi
下面证明:当
a
1
≤
a
2
≤
⋯
≤
a
n
,
b
1
≤
b
2
≤
⋯
≤
b
n
a_1\le a_2\le \dots \le a_n,b_1\le b_2\le \dots \le b_n
a1≤a2≤⋯≤an,b1≤b2≤⋯≤bn时,能构成一个最优决策序列。(事实上,从大到小排序也可)
假设a1,…,an,b1,…,bn取最大值且不按以上顺序排列,设ak,ak+1,bk,bk+1是第一处违反该次序的位置。
- 若ak>ak+1,bk>bk+1,显然交换ak和ak+1,bk和bk+1的次序,取值依旧不变。
- 若ak>ak+1,bk<=bk+1,那么
a k b k + a k + 1 b k + 1 = ( a k + 1 + a k − a k + 1 ) b k + a k + 1 b k + 1 = ( a k + 1 + a k ) b k + a k + 1 ( b k − b k + 1 ) ≤ ( a k + 1 + a k ) b k a_kb_k+a_{k+1}b_{k+1}=(a_{k+1}+a_k-a_{k+1})b_k+a_{k+1}b_{k+1}=(a_{k+1}+a_k)b_k+a_{k+1}(b_k-b{k+1})\le (a_{k+1}+a_k)b_k akbk+ak+1bk+1=(ak+1+ak−ak+1)bk+ak+1bk+1=(ak+1+ak)bk+ak+1(bk−bk+1)≤(ak+1+ak)bk a k b k + 1 + a k + 1 b k = a k b k + 1 + ( a k + 1 + a k − a k ) b k = ( a k + 1 + a k ) b k + a k ( b k + 1 − b k ) ≥ ( a k + 1 + a k ) b k a_kb_{k+1}+a_{k+1}b_k=a_kb_{k+1}+(a_{k+1}+a_k-a_k)b_k=(a_{k+1}+a_k)b_k+a_k(b_{k+1}-b_k)\ge (a_{k+1}+a_k)b_k akbk+1+ak+1bk=akbk+1+(ak+1+ak−ak)bk=(ak+1+ak)bk+ak(bk+1−bk)≥(ak+1+ak)bk
故交换ak和ak+1的顺序,得到一个新的序列,该序列的值大于等于原序列的值。因此一直这样进行下去,最终会得到一个序列,满足 a 1 ≤ a 2 ≤ ⋯ ≤ a n , b 1 ≤ b 2 ≤ ⋯ ≤ b n a_1\le a_2\le \dots \le a_n,b_1\le b_2\le \dots \le b_n a1≤a2≤⋯≤an,b1≤b2≤⋯≤bn且是一个最优决策序列,证毕。
解题步骤
- 将火柴的位置和高度存在结构体数组中,进行排序,排序后实现离散化
- 将a,b组中高度排名相同的火柴的位置编号形成一个1-1映射
- 求映射数组的逆序对(定理:冒泡排序(相邻交换)的最少交换次数等于逆序对个数)
#include<bits/stdc++.h>
using namespace std;
#define int long long
int M =1e8-3;
int a[100005],b[100005],t[100005];
struct node{
int i,h;
}a1[100005],b1[100005];
bool cmp(node x,node y){
return x.h<y.h;//使用升序
}
int ans=0;
void merge(int l,int r){
int aux[r-l+1];
for(int i=l;i<=r;i++){
aux[i-l]=t[i];
}
int mid=(l+r)/2;
int i1=l,i2=mid+1;
for(int i=l;i<=r;i++){
if(i1>mid){
t[i]=aux[i2-l];
i2++;
}else if(i2>r){
t[i]=aux[i1-l];
i1++;
ans+=i2-mid-1;
}else if(aux[i1-l]>aux[i2-l]){
t[i]=aux[i2-l];
i2++;
}else{
t[i]=aux[i1-l];
i1++;
ans+=i2-mid-1;
}
}
}
void mergesort(int l,int r){
if(l>=r) return;
int t=(l+r)/2;
mergesort(l,t);
mergesort(t+1,r);
merge(l,r);
}
signed main(){
int n;
cin>>n;
for(int i=0;i<n;i++){
cin>>a[i];
a1[i].i=i;
a1[i].h=a[i];
}
for(int i=0;i<n;i++){
cin>>b[i];
b1[i].i=i;
b1[i].h=b[i];
}
sort(a1,a1+n,cmp);
sort(b1,b1+n,cmp);
//排序后,结构体数组下标有小到大,对应离散化后的相对高度从小到大
//node.i则反映了火柴的实际位置
for(int i=0;i<n;i++){
t[a1[i].i]=b1[i].i;//a组高度排名第i的火柴,对应b组排名第i的火柴
//t数组的键是该对应关系下a组火柴的实际位置,值是该对应关系下b组火柴的实际位置
//此后任务就是寻找t数组中的逆序对
}
//for(int i=0;i<n;i++) cout<<t[i]<<' ';cout<<endl;
mergesort(0,n-1);
cout<<ans%M;
}