洛谷3322 [SDOI2015]排序(搜索)(剪枝)

题目

洛谷3322 [SDOI2015]排序

题解

搜索+超级剪枝
方案是说有先后顺序之分的,但是操作先后并不影响答案啊。所以对于一个操作次数为k的方案,它的不同顺序的方案数有k!种,这就大大减少了搜索复杂度
所以,我们只要按一定的顺序来枚举,使得每一个组合操作只记录一次就好。其顺序应当从小到大,这样就能使小的区间有序之后再来考虑大的区间。
从小到大枚举dfs到k,表示现在要交换两个大小为2^{k-1}的序列。因为只允许操作一次,所以最多只允许2段长度为2^k的序列是无序的,这个时候考虑交叉交换这两段。如果只有一段无序,那么自己交换就可以了。如果大于2段,那么当前方案无解。
注意到上面提到的段都是2^k的长度来检查的,这样为了应付“每段恰好包括2^{i-1}个数”这个条件的,也是为了降低一半的检查次数,并且不会影响到答案的正确性。这个有点像一道初赛题,大概是“给出2n个数,求至少的比较次数,求出最大和最小值”,不会的自己查度娘。

剪枝原理

数学计数优化搜索,只需搜出一个操作集合,并且知其大小,即可求到由其演变来的方案数。
再有一个就是那道初赛题。。。

代码

#include
#include
#include
using namespace std;
typedef long long ll;
const int MAXN=20;
ll bin[MAXN],fac[MAXN];

int n,a[5000];ll ans=0;

bool check(int x,int k)//判断从x开始长度为bin[k]的序列是否有序 
{
    for(int i=1;in){ans+=fac[now];return ;}
    int t1=0,t2=0;
    for(int i=1;i<=bin[n];i+=bin[k])//比较是按bin[k]来比的 
        if(!check(i,k))
        {
            if(!t1) t1=i;
            else if(!t2) t2=i;
            else return ;
        }
    if(!t1 && !t2) dfs(k+1,now);
    else if(t1 && !t2)
    {
        myswap(t1,t1+bin[k-1],k-1);//交换的是bin[k-1] 
        dfs(k+1,now+1);
        myswap(t1,t1+bin[k-1],k-1);
    }
    else
    {
        for(int x=0;x<=1;x++)
            for(int y=0;y<=1;y++)
            {
                myswap(t1+x*bin[k-1],t2+y*bin[k-1],k-1);
                if(check(t1,k) && check(t2,k))//看看那个交叉互换是合法的 
                {
                    dfs(k+1,now+1);
                    myswap(t1+x*bin[k-1],t2+y*bin[k-1],k-1);
                    break;
                }
                myswap(t1+x*bin[k-1],t2+y*bin[k-1],k-1);
            }
    }
}

int main()
{
    bin[0]=1;for(int i=1;i<20;i++) bin[i]=bin[i-1]<<1;
    fac[0]=1;for(int i=1;i<20;i++) fac[i]=fac[i-1]*i;
    
    scanf("%d",&n);
    for(int i=1;i<=bin[n];i++) scanf("%d",&a[i]);
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
} 

 

你可能感兴趣的:(刷题之路,模拟/搜索,超强剪枝)