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. http请求之of_ordering_getmiditem

    //Public function of_ordering_getmiditem (string as_instr,string as_key) returns string //string as_ ...

  2. C++字符转换等常用方法

    下面是C++中字符编码格式转换(包括ANSI转换为UTF8,UTF8转换为ANSI,ANSI转换为unicode,unicode转换为ANSI,UTF8转换为unicode,unicode转换为UTF ...

  3. Jmeter之Dummy Sampler

    Dummy Sampler(Dummy取样器) Dummy Sampler就像是一个模拟器,需要自己填入请求和响应的信息.(原理类似mock服务) 在没有服务的情况下,测试人员依据Dummy Samp ...

  4. “007~ASP 0104~不允许操作”错误的解决方法(图解)

    今天测试一个Z-Blog程序的上传文件时发现总提示“ 007~ASP 0104~不允许操作 ”的错误,经过度度上各位朋友的帮忙,终于找到解决方法. 这是windows2003 server对上传文件的 ...

  5. .NET CORE API 使用Postman中Post请求获取不到传参问题

    开发中遇到个坑 记录下. 使用Postman请求core api 接口时,按之前的使用方法(form-data , x-www-form-urlencoded)怎么设置都无法访问. 最后采用raw写入 ...

  6. O040、Migrate Instance 操作详解

    参考https://www.cnblogs.com/CloudMan6/p/5538599.html   Migrate 操作的作用是将instance 从当前的计算节点迁移到其他的计算节点上.   ...

  7. Python算法题(二)——国际象棋棋盘(排列组合问题,最小的K个数)

    题目一(输出国际象棋棋盘)  分析: 用i控制行,j来控制列,根据i+j的和的变化来控制输出黑方格,还是白方格.   主要代码: for i in range(8): for j in range(8 ...

  8. 导入java web项目时 web.xml第一行报错

    报错信息: Referenced file contains errors (http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd). For more infor ...

  9. 10 select、poll以及epoll

    IO复用:为了解释这个名词,首先来理解下复用这个概念,复用也就是共用的意思,这样理解还是有些抽象,为此,咱们来理解下复用在通信领域的使用, 在通信领域中为了充分利用网络连接的物理介质,往往在同一条网络 ...

  10. 初识 vue------简单介绍

    /**     vue         作者:尤雨溪         类型:MVVM      准确的来说是MV框架     为什么要学习vue         1.传统的项目是通过操作dom元素来修 ...