火柴排队 题解

1. 题目关键点

这道题其实就是一道求 逆序对 的题

那为什么是求逆序对呢?

我们来分析一下题目:

题目要求交换完以后要 i=1n(aibi)2 最小

拆分得

i=1n(aibi)2=i=1n(ai2+bi22aibi)=i=1n(ai2+bi2)2i=1n(aibi)

因为 ai2+bi2 是固定的,所以最小值就在 aibi 上,即 ai,bi 越大,代数式的值就越小

那当 i=1n(aibi) 最大时,便是本题最优解

那如何取最大值?

这里先给出一个策略(证明放在后面):

对于数列 p1pn,q1qn

max{i=1n(piqi)}=i=1n(piqi)(p1p2p3...pn1pn,q1q2q3...qn1qn)

或者上面公式中 p,q 按降序排列也行。

其实上面公式中所表示的含义就是 顺序之乘 乱序之乘

接下来证明一下 如果有人直接想看代码可以 跳过(bushi

1. 设升序数列 p1pn,q1qn 其中 0<p1<p2,0<q1<q2

2. 推论过程,先给出一个结论:

p1q1+p2q2>p1q2+p2q1

3. 证明这个结论:

移项,得p1q1+p2q2p1q2p2q1>0

合并同类项,得p1(q1q2)+p2(q2q1)>0

转换,得p1(q1q2)p2(q1q2)>0

再合并同类项,得(p1p2)(q1q2)>0

p1<p2,q1<q2p1p2<0,q1q2<0

(p1p2)(q1q2)>0

(p1p2)(q1q2)>0向上推,最终得出

p1q1+p2q2>p1q2+p2q1

因此该公式成立

4. 推广该结论到整个数列中,乱序之乘就相当于不断将交换顺序打乱的过程,最终符合该结论的标准,最终得出 顺序之乘 乱序之乘证毕

那么,证明好这个结论,我们继续往下看

由题目要 i=1n(aibi) 最大,我们由上面的公式推导可以想出:先给 a,b 分别从小到大排序,让 a 中第一小的数对 b 中第一小的数,让 a 中第二小的数对 b 中第二小的数……以此类推

那最小距离就出来了,可是我们怎么求出最小交换次数呢?

我们举一个例子 其实就是样例1

4
2 3 1 4
3 2 1 4

我们由样列知,a 数组为 a={2,3,1,4}b 数组为 b={3,2,1,4}

对两个数组分别从小到大排序,得

信息 数组 a b
排序过后的值 {1,2,3,4} {1,2,3,4}
该数组中该值对应的原数组的编号 {3,1,2,4} {3,2,1,4}

我们来依次处理一下:

首先看 a1 的编号与 b1 的编号,都是 3,因此不用管

再来看一下 a2 的编号与 b2 的编号,a2 编号是 1b2 编号是 2,是一个逆序对,不符合“相同排名一一对应”的原则

这里,产生了不符合原则的数对,所以我们可以另外定义一个数组 ma,以进行演算。这也是本程序的核心

我们看一下 ma 如何工作的:

首先,设 mabi的编号=ai的编号

还是以上面的样例为例,操作是这样的:

  1. mab1的编号=a1的编号ma3=3
  2. mab2的编号=a2的编号ma2=1
  3. mab3的编号=a3的编号ma1=2
  4. mab4的编号=a4的编号ma4=4
  5. 所以 ma={2,1,3,4},逆序对个数为 1(交换 ma1ma2

看,这就是 排序 的魅力!

为什么上述操作能够实现?

因为产生了 逆序:只要原序列中的 aibi 的排名是一一对应的,那么排序以后两个数的 相对位置不变,因此不会产生逆序;而如果不是一一对应的,排完序两个数的 相对位置就会发生变化,因而产生逆序对

求逆序对有多种方法:冒泡、归并、树状数组等

这里由于 本蒟蒻太菜,只会冒泡和归并,冒泡又会超时,所以用归并写的

如果不知道归并或想用树状数组写可以自行 出门右转 百度

好了,那么思路就推导完了,接下来如果读者已经懂了可以自己尝试写一下

如果还想看代码,也可以往后面看(bushi


2. 代码实现

本部分将分步骤写成

2.1 定义与模板

这部分比较简单,代码里有注释

//#pragma GCC optimize(3)
//#pragma GCC optimize(2)
//上方是O(2)与O(3)的优化,如果超时可以尝试开一下(但是本程序不会)
//要注意的是,上方代码不可以再洛谷取消注释后提交,不然会CE(别问我怎么知道的)
#include<bits/stdc++.h>//万能头
using namespace std;//命名空间
const int mod=99999997;//按题目定义的模数
int n,ans=0;//ans指最少要交换几次
struct match{//定义结构体match(火柴)
int high,id;//high指火柴高度,id指火柴排序后在原来数列的编号
inline bool operator < (const match &temp)//重载运算符,不会的自己百度
const{return high<temp.high;}//按照高度从小到大排序
}a[100009],b[100009];
int ma[100009],temp[100009];//ma的定义在上文,temp是对ma归并排序时的临时数组
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0); //玄学的cin,cout优化
return 0;
}

2.2 预处理

预处理包含输入、离散化、排序、初始化好 ma 数组的值

//#pragma GCC optimize(3)
//#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int mod=99999997;
int n,ans=0;
struct match{
int high,id;
inline bool operator < (const match &temp)
const{return high<temp.high;}
}a[100009],b[100009];
int ma[100009],temp[100009];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].high,a[i].id=i;
for(int i=1;i<=n;i++) cin>>b[i].high,b[i].id=i;//输入,由于数据太大,需要离散化
sort(a+1,a+n+1),sort(b+1,b+n+1);//对火柴排序
for(int i=1;i<=n;i++) ma[b[i].id]=a[i].id;//ma的用法上问所示
return 0;
}

2.3 归并排序求逆序对及输出

归并排序……都写到 蓝题 就不用我讲了吧……

不过如果想知道归并排序可以看看 这些

只是模板罢了

//#pragma GCC optimize(3)
//#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int mod=99999997;
int n,ans=0;
struct match{
int high,id;
inline bool operator < (const match &temp)
const{return high<temp.high;}
}a[100009],b[100009];
int ma[100009],temp[100009];
inline void Sort(int l,int r)//归并排序模板,我就不多说了
{
if(l>=r) return;
int mid=(l+r)>>1;
Sort(l,mid),Sort(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r)
{
if(ma[i]<=ma[j]) temp[k++]=ma[i++];
else temp[k++]=ma[j++],ans+=(mid-i+1)%mod,ans%=mod;
}
while(i<=mid) temp[k++]=ma[i++];
while(j<=r) temp[k++]=ma[j++];
for(int point=l;point<=r;point++) ma[point]=temp[point];
return;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].high,a[i].id=i;
for(int i=1;i<=n;i++) cin>>b[i].high,b[i].id=i;
sort(a+1,a+n+1),sort(b+1,b+n+1);
for(int i=1;i<=n;i++) ma[b[i].id]=a[i].id;
Sort(1,n);//对ma进行归并
cout<<ans;//输出,不需要我讲了吧
return 0;
}

2.4 最终无注释 高清 代码

这个我就不放在这里了吧,毕竟前面已经演示过很多次了

想知道的可以看 这里


3. 总结

知识点:不等式、归并求逆序对、离散化等

题目涉及知识点不难,只是要慢慢细心地推,发现求逆序对的 本质


THE END


posted @   DreamerX  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示