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. golang 环境配置 over centos7

    基本编程环境 下载go1.8压缩包,解压后存放至目录~/work/下. 编辑~/.bashrc,添加如下三行在尾部 执行 source ~/.bashrc  配置vim + go  下载Vundle ...

  2. Redis服务端相关

    全局命令: 查看所有键: keys * 键总数: dbsize 检查键是否存在: exists key 删除键: del key [key...] 键过期: expire key seconds 键的 ...

  3. 算法:二叉树的层次遍历(递归实现+非递归实现,lua)

    二叉树知识参考:深入学习二叉树(一) 二叉树基础 递归实现层次遍历算法参考:[面经]用递归方法对二叉树进行层次遍历 && 二叉树深度 上面第一篇基础写得不错,不了解二叉树的值得一看. ...

  4. JS基础_流程控制语句

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. python之uWSGI和WSGI

    WSGI协议 首先弄清下面几个概念:WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述web ...

  6. maven中如何将所有引用的jar包打包到一个jar中

    在pom文件的build节点中添加这个插件的引用: <plugins> <plugin> <artifactId>maven-assembly-plugin< ...

  7. Cesium中的样条插值

    Cesium中的样条插值 在cesium里,提供了三种样条插值方法,LinearSpline,CatmullRomSpline,HermiteSpline.在具体的实例上,可以使用样条插值法利用已知的 ...

  8. windows 下Nginx 入门

    验证配置是否正确: nginx -t 查看Nginx的版本号:nginx -V 启动Nginx:start nginx 快速停止或关闭Nginx:nginx -s stop 正常停止或关闭Nginx: ...

  9. 获取iframe子页面内容高度给iframe动态设置高度

    <!DOCTYPE html><html> <head> <meta charset="UTF-8" /> <meta nam ...

  10. pandas中的Series

    我们使用pandas经常会用到其下面的一个类:Series,那么这个类都有哪些方法呢?另外Series和DataFrame都继承了NDFrame这个类,df.to_sql()这个方法其实就是NDFra ...