Description

小A有一个1-2^N的排列A[1..2^N],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的i(1<=i<=N),第i中操作为将序列从左到右划分为2^{N-i+1}段,每段恰好包括2^{i-1}个数,然后整体交换其中两段.小A想知道可以将数组A从小到大排序的不同的操作序列有多少个,小A认为两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同).

  下面是一个操作事例:
  N=3,A[1..8]=[3,6,1,2,7,8,5,4].
  第一次操作,执行第3种操作,交换A[1..4]和A[5..8],交换后的A[1..8]为[7,8,5,4,3,6,1,2].
  第二次操作,执行第1种操作,交换A[3]和A[5],交换后的A[1..8]为[7,8,3,4,5,6,1,2].
  第三次操作,执行第2中操作,交换A[1..2]和A[7..8],交换后的A[1..8]为[1,2,3,4,5,6,7,8].

Input

第一行,一个整数N

第二行,2^N个整数,A[1..2^N]

Output

一个整数表示答案

Sample Input

3
7 8 5 6 1 2 4 3

Sample Output

6

HINT

100%的数据, 1<=N<=12.

题解:

......搜索送命题

对于菜得不能再菜的垃圾博主,搜索真是这世界上最难的算法(dalao勿喷)。

考试时看到这题,觉得是道数学题或者是一道数据结构加数学题,结果看题解发现是一道搜索。

不会写搜索的垃圾博主当场吓尿。

但考试时输出阶乘qj测试点得了15pts,运气不错

咳咳...言归正传

其实首先要想到一个性质就是对于一个操作序列的顺序是不影响他的合法性的,也就是说每次多加一种操作对答案的贡献是阶乘的这也就是我没爆零的原因。

那么我们就只需要确定每个操作是不是要选就可以了。

我们从小到大dfs,dfs(now,x)表示选了now种方法,现在判断第x种方法。

显然递归入口dfs(0,1)。

然后我们考虑怎样剪枝。

实际上交换的过程就是把不合法的子序列进行交换得到合法的子序列。

然后我们就可以每次扫一遍,找出对于每次操作x,不合法的序列个数及不合法序列的开始位置以便交换。

如果有两个以上不合法的子序列那就完戏了,直接return。

如果没有不合法的子序列,那么就不需要进行这种操作,直接搜下一层。

如果有一个这种子序列,那么交换他的前一半和后一半,再判断是否合法,再进行搜索。

如果有两个这种子序列,那么判断四种情况,在进行搜索。

很多神犇只说四种情况,并没有说明白是哪四种情况,坑害了我这种懵逼的蒟蒻

其实也很好想就是判断前一段的前一段和后一段的前一段交换,前一段的后一段和后一段的前一段,前一段的前一段和后一段的后一段,前一段的后一段和后一段的前一段。

就似这四种情况辣。

另外还有要注意的是就是在写交换和判断两个过程时一定要处理好循环的边界问题,博主就因为这个被卡了好长时间。

(附上本人丑陋的代码)、

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
int fac[],power[];int n;int num;
int a[<<|];
int res=;
int qpower(int a,int b){
int ans=;
while(b){
if(b&) ans=ans*a;
b>>=;
a=a*a;
}
return ans;
}
int check(int x,int k){
for(int i=;i<power[k];i++){//不要右边界
if(a[x+i]!=a[x+i-]+){
return ;
}
}
return ;
}
void swap(int x,int y,int k){
for(int i=;i<power[k];i++){//左右边界都不要
int t;
t=a[x+i];
a[x+i]=a[y+i];
a[y+i]=t;
}
}
void dfs(int now,int x){
//cout<<x<<" "<<now<<endl;
if(x==n+){
res+=fac[now];
return ;
}
int pos1=;int pos2=;int cnt=;
for(int i=;i<=num;i+=power[x]){
if(check(i,x)){
cnt++;
if(cnt>=) break;
if(!pos2){
if(!pos1){
pos1=i;
}
else pos2=i;
}
}
}
//cout<<cnt<<" "<<pos1<<" "<<pos2<<endl;
if(cnt>) return ;
if(cnt==){
dfs(now,x+);
}
if(cnt==){
swap(pos1,pos1+power[x-],x-);
dfs(now+,x+);
swap(pos1,pos1+power[x-],x-); }
if(cnt==){
swap(pos1,pos2,x-);
if(check(pos1,x)||check(pos2,x)){
swap(pos1,pos2,x-);
}
else{
dfs(now+,x+);
swap(pos1,pos2,x-);
}
swap(pos1,pos2+power[x-],x-);
if(check(pos1,x)||check(pos2,x)){
swap(pos1,pos2+power[x-],x-);
}
else{
dfs(now+,x+);
swap(pos1,pos2+power[x-],x-);
}
swap(pos1+power[x-],pos2,x-);
if(check(pos1,x)||check(pos2,x)){
swap(pos1+power[x-],pos2,x-);
}
else{
dfs(now+,x+);
swap(pos1+power[x-],pos2,x-);
}
swap(pos1+power[x-],pos2+power[x-],x-);
if(check(pos1,x)||check(pos2,x)){
swap(pos1+power[x-],pos2+power[x-],x-);
}
else{
dfs(now+,x+);
swap(pos1+power[x-],pos2+power[x-],x-);
}
}
}
int main(){
fac[]=;power[]=;
for(int i=;i<=;i++){
fac[i]=fac[i-]*i;
power[i]=power[i-]<<;
}
scanf("%d",&n);
num=qpower(,n);
for(int i=;i<=num;i++){
scanf("%d",&a[i]);
}
dfs(,);
//for(int i=1;i<=15;i++) cout<<power[i]<<" "<<fac[i]<<endl;
//cout<<power[15];
printf("%d",res);
}

丑陋得不能再丑陋的代码

一道看题解后完全自己码出来,调出来的代码,还是很高兴的。

搜索水平还是太弱了啊,考试时都不知道暴力怎么打,一定要提升搜索的水平啊

[sdoi2015]排序(搜索+剪枝优化)的更多相关文章

  1. BZOJ 3990: [SDOI2015]排序 [搜索]

    3990: [SDOI2015]排序 题意:\(2^n\)的一个排列,给你n种操作,第i种把每\(2^{i-1}\)个数看成一段,交换任意两段.问是这个序列有序的操作方案数,两个操作序列不同,当且仅当 ...

  2. poj 1054 The Troublesome Frog (暴力搜索 + 剪枝优化)

    题目链接 看到分类里是dp,结果想了半天,也没想出来,搜了一下题解,全是暴力! 不过剪枝很重要,下面我的代码 266ms. 题意: 在一个矩阵方格里面,青蛙在里面跳,但是青蛙每一步都是等长的跳, 从一 ...

  3. [bzoj3990][SDOI2015]排序-搜索

    Brief Description 小A有一个1-2^N的排列A[1..2^N],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的i(1<=i<= ...

  4. BZOJ 3990 [SDOI2015]排序 ——搜索

    [题目分析] 可以发现,操作的先后顺序是不影响结果的,那么答案就是n!的和. 可以从小的步骤开始搜索,使得每一个当前最小的块都是上升的数列,然后看看是否可行即可. 复杂度好像是4^n [代码](哪里写 ...

  5. BZOJ 3990: [SDOI2015]排序(搜索+剪枝)

    [SDOI2015]排序 Description 小A有一个1-2^N的排列A[1..2^N],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的i(1< ...

  6. 搜索(剪枝优化):HDU 5113 Black And White

    Description In mathematics, the four color theorem, or the four color map theorem, states that, give ...

  7. 图解Leetcode组合总和系列——回溯(剪枝优化)+动态规划

    Leetcode组合总和系列--回溯(剪枝优化)+动态规划 组合总和 I 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 ...

  8. hdu 5887 搜索+剪枝

    Herbs Gathering Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  9. 搜索+剪枝——POJ 1011 Sticks

    搜索+剪枝--POJ 1011 Sticks 博客分类: 算法 非常经典的搜索题目,第一次做还是暑假集训的时候,前天又把它翻了出来 本来是想找点手感的,不想在原先思路的基础上,竟把它做出来了而且还是0 ...

随机推荐

  1. k8s-高可用架构设计

    docker的私有仓库harbor.容器化kubernetes部分组建.使用阿里云日志服务收集日志. 部署完成后,你将理解系统各组件的交互原理,进而能快速解决实际问题,所以本文档主要适合于那些有一定k ...

  2. 25-Perl CGI编程

    1.Perl CGI编程什么是CGICGI 目前由NCSA维护,NCSA定义CGI如下:CGI(Common Gateway Interface),通用网关接口,它是一段程序,运行在服务器上如:HTT ...

  3. 【原创】大叔经验分享(57)hue启动coordinator时报错

    hue启动coordinator时报错,页面返回undefinied错误框: 后台日志报错: runcpserver.log [13/May/2019 04:34:55 -0700] middlewa ...

  4. 安装多个ORACLE导致多个Oracle HOME的情况!

    Oracle由于版本的不同,在注册表中产生的注册表信息也有所不同,但主要的键值信息还是一样的,例如Oracle10g比oracle9i在注册表中表现的更为“简洁”,在未知的情况下,获取Oracle10 ...

  5. export CommonJS AMD ES6

    export https://www.cnblogs.com/fayin/p/6831071.html 导入文件: a  -  b  -  c  ,对象隔代消失,可转成函数返回  导入模块对象(命名) ...

  6. LInux基于nginx与OpenSSL实现https访问

    注意!!首先在nginx安装时添加--with-http_ssl_module模块,否则将会报错,只能从头开始了 自建证书: 通过openssl命令(软件包:openssl :openssl-deve ...

  7. 关于MySQL服务无法正常启动问题

    使用mysql的时候,突然查看服务列表也找不到mysql服务 解决办法: 一.首先打开CMD,切换到MySql安装目录的MySql Server →bin目录下 运行如下命令(具体试个人安装的MySq ...

  8. apache2.4.9编译安装

    源码编译安装 由于centos7的版本可以支撑所以在centos6上编译安装 centos6 准备 gzip wget 安装 yum install gzip wget -y apr . apr-ut ...

  9. VToRay C-S config

    Server config: { "inbounds": [{ "port": 20000, //Server Listening Port "pro ...

  10. 二、Nginx多站点配置(参考宝塔的)分析

    一.基于宝塔配置文件分析(站的配置文件) 新增的站点配置即添加server并包含在nginx内 查找文件: 文件内容: 二.伪静态 伪静态是一种可以把文件后缀改成任何可能的一种方法,如果我想把php文 ...