【排序不等式+离散化+逆序对】火柴排队

洛谷p1966

算法分析

最小化 ∑ ( 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) (aibi)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 a1a2an,b1b2bn时,能构成一个最优决策序列。(事实上,从大到小排序也可)
假设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+akak+1bk+ak+1bk+1=(ak+1+ak)bk+ak+1(bkbk+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+akak)bk=(ak+1+ak)bk+ak(bk+1bk)(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 a1a2an,b1b2bn且是一个最优决策序列,证毕。

解题步骤

  • 将火柴的位置和高度存在结构体数组中,进行排序,排序后实现离散化
  • 将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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值