SDOI2015 排序
解法:
先打个暴力,可以发现一个重要结论!
对于一个操作序列,如果它是合法的,那么它的全排列都是合法的。所以我们操作区间从小到大进行操作。
对于每次操作,假如我们交换的序列长度为T,那么我们将整个序列按每段2T分成多段,对于每一段,由于我们是从小到大操作,之后的操作至少都是2T的整段交换,所以这2T个数内部的相对关系在以后不会发生改变。如果要使序列合法,在这次操作后,我们至少要使每2T个数是连续递增的。
对于每次操作,我们先扫一遍,看有多少个2T的区间是不合法的。
如果不合法的区间数大于2,那么无解
如果没有不合法的区间,那么不需要进行这个长度的操作,直接跳到下一层。
如果区间数为1或2,暴力修改交换看是否能达成合法状态。(1则前后交换,2则2个区间之间进行交换,有4种方案)
你写的丑一点也是O(4^n),可以通过本题
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> using namespace std; typedef long long ll; ll jc[13],Ans; int a[4201]; int n,ws,i; bool Can() { int i; for(i=1;i<=n;i++)if(a[i]!=i)return false; return true; } bool check(int x,int i) { int nxt,lst; lst=(i-1)*x+x/2; nxt=lst+1; if(a[nxt]!=a[lst]+1)return false; else return true; } void dfs(int dep,int kf) { bool pp; int dif[3]; int one,num,chk,tx,ty,i,j,l; if(Can()){ Ans+=jc[kf]; return; } if(dep==ws+1)return; one=1<<dep; num=n/one; chk=0; for(i=1;i<=num;i++)if(check(one,i)==false){ chk++; if(chk>2)return; dif[chk]=i; } if(chk==0)dfs(dep+1,kf); if(chk>2)return; if(chk==1){ tx=(dif[chk]-1)*one+1; ty=tx+one/2; for(i=1;i<=one/2;i++)swap(a[tx+i-1],a[ty+i-1]); dfs(dep+1,kf+1); for(i=1;i<=one/2;i++)swap(a[tx+i-1],a[ty+i-1]); } if(chk==2){ for(i=1;i<=2;i++) for(j=1;j<=2;j++){ if(i==1)tx=(dif[1]-1)*one+1; else tx=(dif[1]-1)*one+1+one/2; if(j==1)ty=(dif[2]-1)*one+1; else ty=(dif[2]-1)*one+1+one/2; for(l=1;l<=one/2;l++)swap(a[tx+l-1],a[ty+l-1]); pp=check(one,dif[1])&check(one,dif[2]); if(pp)dfs(dep+1,kf+1); for(l=1;l<=one/2;l++)swap(a[tx+l-1],a[ty+l-1]); } } } int main() { scanf("%d",&ws); jc[0]=1; for(i=1;i<=ws;i++)jc[i]=jc[i-1]*i; n=1<<ws; for(i=1;i<=n;i++)scanf("%d",&a[i]); dfs(1,0); printf("%lld\n",Ans); }