洛谷题目 P1966 火柴排队

博客介绍了洛谷题目P1966——火柴排队,通过数学分析得出最优策略是让大的数与大的数配对,小的数与小的数配对。接着,利用逆序对的概念,讨论如何通过最少的交换次数使两排火柴顺序相同,并给出使用逆序对统计和归并排序优化算法的解决方案。
摘要由CSDN通过智能技术生成

这里是题目:题目传送门
这道题,乍一看上去,其实根本没有什么思路。

首先,先来一波玄学证明:

∑(ai−bi)2=∑(ai2+bi^2)-2∑(ai+bi)

首先,前面的平方的和是固定不变的,也就是说,要使两排火柴之间的距离尽量小,就要使2∑(ai+bi)尽量大,也就是说,大的a[i]配大的b[i],小的a[i]配小的b[i],这又是为什么捏?
我们拿2个数来看,假设第一排中a<b,第二排中c<d,那么a
c+b*d一定是最大的。

ac+bd-(ad+bc)=(a-b)(c-d)>0

同理可以推到每一排k个数也是这样的(问我怎么证明?以k为例子,首先,我们可以得到每排前面k-1个排出的最大值,之后将其看作一个整体,与每一排的第k个数配对,这就是一个每排就只有两个数的问题了,递推下去就可以证明结论成立了),其实就是用到了数学归纳法。

得到了这个结论之后,要想使两排大小的排序顺序相同,依然不是一道简单的题目。

首先,注意到只需要看顺序问题,于是想到先要运用到拆分的思想,不管原来的数是多少。只关心在这一排中是第几大的。于是我们用rank[i]表示第i大的数在原数组里面是第几个,之后,我们就得到了两行的排序情况。

之后,我们又怎么通过最少次数的操作,使得两行的排序情况相同捏?考虑第二排的情况,我们看第二排的第i个在第一排中所对应的位置,然后与自己本身的rank值进行比较,得到最小的交换次数就是两个数组之间的逆序对的数量

接下来,又是一波玄学证明:

为什么最小的交换次数就是两个数对的逆序对的数量捏?首先,我们要证明一件事请,就是,假如还有逆序对存在,那么就必定会有两个相邻的点是逆序对:
假设相邻的两个点之间都不存在逆序对,那么对于所有的i来讲,a[i]<a[i+1]。因此我们可以得到该序列一定是单调递增的,这时候,是没有逆序对的。
所以,我们一定可以找到两个相邻的点,这两点之间就是一个逆序对。点击一次,就将这两个数调换位置。之后再来考虑交换完这两个相邻的数之后,逆序对减少的情况。
假设a[i]与a[i+1]交换之前,a[i]与前面k1个数形成了逆序对,a[i]与后面k2个数形成了逆序对,交换完之后,后面的逆序对减少了1(就是a[i],a[i+1])这一对,同理,a[i+1]也少了a[i],a[i+1]这一对,但是由于二者是相同的所以总共减少了一组逆序对。

之后,还有一个问题,就是在统计有多少组逆序对的时候,不要枚举每一个点,然后再求出相对应的逆序对,这样的时间法度是O(n^2)的,最终会超时,我们可以使用归并排序,在排序的过程中,和并一次就统计一下逆序对的个数,最后再加起来,这样的时间法度是O(nlog(n))级别的。

代码如下:

#include<iostream>
#include<cstdlib>
#include<utility>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxN=100100;
const int mod=99999997;
int n,ans;
int a[maxN][2],rank[maxN][2];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值