2024.11.14 鲜花

双调排序的正确性证明暨第八交响曲题解

推歌:Double Agent

好多题解都写的或多或少有问题(包括那篇 30 分钟速通),终于是整明白了。

首先给出双调序列的定义:满足一下条件之一的序列

  1. k,i<k,ai>ai+1i>k,ai<ai+1 即是单谷。
  2. 其可以通过循环位移满足 1

注意到根据定义 5,6,7,6,5,1,2,3 是双调序列,因为其可以通过循环位移成 6,5,1,2,3,5,6,7,这里有很多题解都是错的,大多是说是单峰或单谷,GGrun 有 hack 1,2,8,7,6,5,4,3

upd:这个 hack 是双调,或者说所有的单峰和单谷都是双调,hack 的地方是将它拆开后 1,2,4,3|6,5,8,76,5,8,7 不是单峰或单谷,但它是双调

过程不再解释,但是需要指出的是,双调序列满足从中间分成相等两段后依次经过 Batcher 比较器(即 Compare And Swap 操作)后满足左右两段都是双调序列并且左边所有值都小于右边所有值,并不需要重新构造。

如果这个结论是真的,那么整个算法的正确性是显然的,考虑证明这个结论。

首先应用高德纳的 01 原理,即对于任意排序网络,若其能对 0/1 序列排序,则其能对所有序列排序。

证明大致就是考虑二进制分解,其对于每一位都能排对。

upd:贺了一个好像更正规的证明

考虑其逆否命题:任何不正确的排序网络都可以构造一种 0/1 序列使其无法排序,这里给出一个构造。

考虑一个可以使其错误的序列 a,找到其中一个逆序对 (i,j),将 ai 的数设为 1,将 aj 的设为 0

考虑一对一样的数可以视为交换,也可以视为不交换,所以将他扔进这个排序网络中可以视为其依然只交换原来的交换,(i,j) 依然还是一个逆序对。

考虑一个双调的 0/1 序列,容易发现其收尾相接一定最多只有连续的一段 1

对于没有 10,算法显然正确。

然后考虑分讨 0/1 位置和划分区间,下面用 00011|10000 序列 0001110000 分成 0001110000 两段。

  1. 形如 0011111000 的序列,即 1 是连续一段:

    • 00001|11000 即划分到 11 中间:

      容易发现其左边一定是前缀,右面一定是后缀,所以若两边的按位与不为 0,则经过交换后右边一定是连续的一段 1,左边经过将一定后缀变成 0 也一定最多存在一段连续的 1,满足定义。

      若按位或是 0,其左边一定是 0,右边一定是一段前缀加一段后缀。

    • 00110|00000 即没有划分到 11 中间:

      容易发现其经过交换后右边一定是连续的一段 1,左边一定全是 0,满足定义。

类似分讨 11100011,即是前后缀的划分点,容易证明其算法的正确性。

Code
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using llf=long double;
using ull=unsigned long long;
#define endl '\n'
#ifdef LOCAL
FILE *InFile=freopen("in_out/in.in","r",stdin),*OutFile=freopen("in_out/out.out","w",stdout);
#else
FILE *InFile=freopen("symphony.in","r",stdin),*OutFile=freopen("symphony.out","w",stdout);
#endif
const int N=203;
int n,md;
struct V{int l,r,d; V(int a,int b,int c):l(a),r(b),d(c){}};
vector<V> aas[13];
void Srt(int l,int r,int d,vector<V> &c){
if(l==r) return ;
else{
int mid=l+r>>1;
for(int a=l,b=mid+1;a<=mid&&b<=r;++a,++b) c.emplace_back(a,b,d);
Srt(l,mid,d+1,c),Srt(mid+1,r,d+1,c);
}
}
void PSrt(int l,int r,int d,vector<V> &c){
int mid=l+r>>1;
for(int a=mid,b=mid+1;b<=r&&a>=l;--a,++b) c.emplace_back(a,b,d);
Srt(l,mid,d+1,c),Srt(mid+1,r,d+1,c);
}
void Slv(int l,int r,int d){
if(l==r) return ;
else{
int mid=l+r>>1; md=max(md,d);
Slv(l,mid,d+1),Slv(mid+1,r,d+1);
PSrt(l,r,0,aas[d]);
}
}
int main(){
ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
cin>>n; int r=1; while(r<n) r<<=1;
Slv(1,r,1);
int cnt=0;
for(int i=md;i;--i){
sort(aas[i].begin(),aas[i].end(),[](const V &a,const V &b){return a.d<b.d;});
int la=0;
for(auto k:aas[i]) if(k.r<=n) if(la!=k.d) ++cnt,la=k.d;
++cnt;
}
cout<<cnt<<endl;
for(int i=md;i;--i){
int la=0;
for(auto k:aas[i]) if(k.r<=n){
if(la!=k.d) cout<<endl,la=k.d;
cout<<"CMPSWP R"<<k.l<<" R"<<k.r<<' ';
}
cout<<endl;
}
}
P


posted @   5k_sync_closer  阅读(97)  评论(10编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示