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

来源:互联网 发布:算命最准的软件 编辑:程序博客网 时间:2024/06/05 06:58

题目

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

题解

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

总结

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

代码

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#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;}
原创粉丝点击