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. raspberrypi 树莓派 内核编译

    相关版本信息 硬件:树莓派 2b 目标系统: linux 编译环境:ubuntu 14.4 32bit 用户路径:/home/hi/ 安装交叉编译链 cdmkdir pi/kernelcd pi/ke ...

  2. redis 事务 事务机制详解 MULTI、EXEC、DISCARD、WATCH

    1. Redis服务端是个单线程的架构,不同的Client虽然看似可以同时保持连接,但发出去的命令是序列化执行的,这在通常的数据库理论下是最高级别的隔离2. 用MULTI/EXEC 来把多个命令组装成 ...

  3. Tensorflow加载预训练模型和保存模型

    转载自:https://blog.csdn.net/huachao1001/article/details/78501928 使用tensorflow过程中,训练结束后我们需要用到模型文件.有时候,我 ...

  4. pb datawindow 类型

    DataWindow.Processing 判断 DataWindow 对象的类型 可用 DataWindow.Processing 判断 DataWindow 对象的类型,dw的类型如下:     ...

  5. C语言写郑州大学校友通讯录

    #include <stdio.h> #include <string.h> #include <stdlib.h> #define LEN sizeof(stru ...

  6. Sql Server--如何自动备份数据

    下面我来讲一下如何通过维护计划来实现完整备份+差异备份: (1)在SSMS的对象资源管理器中右击“维护计划”,选择“维护计划向导”,系统将弹出向导窗口,如图: 这里向导已经告诉我们维护计划到底能够干什 ...

  7. ZROI Day1 比赛解题报告

    ZROI Day1 比赛解题报告 版权原因不提供题面相关信息 序 前天晚上搞得比较晚,然后早上做题很没状态,刚看到T1发现没什么思路就有点慌,赶紧看了看T2,T3, 发现T3暴力很好打,T2想了一想可 ...

  8. Springboot Hikari Centos 首次连数据库很慢

    前言: springboot项目默认使用了hikari作为数据库连接池,在开发机器上一切正常. 但是把程序部署到Centos7 x64上之后发现,每次首页登录的时候都要停顿几十秒. 于是,在程序启动后 ...

  9. 销售订单(SO)-API-登记销售订单

    登记销售订单可以在新增订单的时候就登记:并不是去修改 flow_status 为booked,而是赋值action request:就下面两句 l_action_request_tbl(l_actio ...

  10. 7、Nginx基础Http原理

    1Http协议概述 HTTP全称HyperText Transfer Protocol中文名为超文本传输协议 1.1.什么是超文本? 包含有超链接(Link)和各种多媒体元素标记的文本.这些超文本文件 ...