搜索好题。spa
这一题中有不少显而易见的结论。。。真的,没发现或者没有意会到会很麻烦。。。code
首先,操做序列能够乱序,也就是说,对于一个操做集合(元素个数为\(N\))对应成立的操做序列个数为\(N!\)排序
而后咱们考虑从小到大分段交换,在这个过程当中间,咱们只要保证每一段是连续递增的就能够保证最终结果是连续递增的。get
不妨设当前分的段为\(K\)。it
咱们须要验证一下在分段长为\(K-1\)时交换的是否知足每一段是连续递增。class
而后再考虑当前\(K\)段中,有几段没有递增。搜索
抠出全部没递增的而后两两交换?集合
实测能够,可是咱们要剪枝,由于这是一道搜索di
这里又是一个剪枝:若是超过\(4\)段乱序就能够退出了,交换不出来的。
总而言之,这一题就是一道分析加剪枝的毒瘤搜索题。
#include<bits/stdc++.h> using namespace std; #define int long long inline int read() { int f=1,w=0;char x=0; while(x<'0'||x>'9') {if(x=='-') f=-1; x=getchar();} while(x!=EOF&&x>='0'&&x<='9') {w=(w<<3)+(w<<1)+(x^48);x=getchar();} return w*f; } const int N=1LL<<15; int n,Fac[20],A[N],Ans; inline bool Check(int K) { for(int i=1;i<=1<<(n-K);i++) if(A[(i-1)*(1<<K)+1]+(1<<(K-1))!=A[(i-1)*(1<<K)+1+(1<<(K-1))]) return 0; return 1; } inline void SWAP(int i,int j,int K) { for(int k=1;k<=1<<K;k++) swap(A[i+k-1],A[j+k-1]); } inline void Dfs(int K,int Cnt) { int Tmp[5],Sum=0; if(K&&!Check(K)) return ; if(K==n) {Ans+=Fac[Cnt];return ;} Dfs(K+1,Cnt); for(int i=1;i<=1<<(n-K);i+=2) if(A[(i-1)*(1<<K)+1]+(1<<K)!=A[(i)*(1<<K)+1]) {if(Sum==4) return ;Tmp[++Sum]=i,Tmp[++Sum]=i+1;} if(!Sum||Sum>4) return ; for(int i=1;i<=Sum;i++) for(int j=i+1;j<=Sum;j++) { SWAP((Tmp[i]-1)*(1<<K)+1,(Tmp[j]-1)*(1<<K)+1,K); Dfs(K+1,Cnt+1); SWAP((Tmp[i]-1)*(1<<K)+1,(Tmp[j]-1)*(1<<K)+1,K); } } signed main(){ #ifndef ONLINE_JUDGE freopen("A.in","r",stdin); #endif n=read(),Fac[0]=1; for(int i=1;i<=12;i++) Fac[i]=Fac[i-1]*i; for(int i=1;i<=(1<<n);i++) A[i]=read(); Dfs(0,0);printf("%lld",Ans); }