JZOJ 5441. 【NOIP2017提高A组冲刺11.1】序列

题目

给定一个1~n的排列x,每次你可以将x1~xi翻转。你需要求出将序列变为升序的最小操作次数。
数据范围
n≤25。

题解

对于n≤8的情况,完全可以双向广搜。考察hash和bfs的基本功。
左边的大神一看数据范围就知道是搜索题。而我当时想到的也是搜索,但是剪枝没想到,所以觉得搜索不能过。
然而事实并不是这样。
由于步数最多为 2n ,所以步数很小(解的深度较浅),可以考虑迭代加深。
有东西变了:一些数原来的位置。
有东西没变:靠后的数位置不变。
思维拓展:|ai-aj|>1的对数可能变了,且操作一次对数最多-1,而最后序列的|ai-aj|>1的对数为0。所以得出一个剪枝:当前步数+|ai-aj|>1的对数如果大于期望步数那么就是不可能的。
为什么这个剪枝这么强大?
如果答案越深,那么拓展的情况相对于深度小的状态多得多,那么被剪枝的可能性越大

总结

对于搜索题:
什么东西在变,什么东西没有变,大胆地把他们找出来,这样方便剪枝。
②觉得它是搜索,不要觉得暂时没有什么剪枝就放弃,一定会有的,只是没有发现而已。

代码

#include
#include
#include
#include
#include
#define N 30
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,ds,n,T,ans;
int a[N];
bool ia;
bool pd(){
    int i;
    fo(i,1,n)if(a[i]!=i)return 0;
    return 1;
}
void rev(int x){
    int i;
    fo(i,1,x>>1)swap(a[i],a[x-i+1]);
}
void dg(int x,int y){
    if(x+y>ans)return;
    int i,j;
    if(pd()){
        ia=1;
        return;
    }
    if(ia)return;
    fo(i,2,n){
        j=y+(abs(a[i+1]-a[i])==1)-(abs(a[i+1]-a[1])==1);
        rev(i);
        dg(x+1,j);
        if(ia)return;
        rev(i);
    }
}
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        fo(i,1,n)scanf("%d",&a[i]);
        ds=0;
        fo(i,1,n-1)if(abs(a[i]-a[i+1])>1)ds++;
        a[n+1]=-1;
        fo(ans,0,2*n){
            ia=0;
            dg(0,ds);
            if(ia){
                printf("%d\n",ans);
                break;
            }
        }
    }
    return 0;
}

你可能感兴趣的:(JZOJ 5441. 【NOIP2017提高A组冲刺11.1】序列)