JZOJ5441. 【NOIP2017提高A组冲刺11.1】序列 启发式搜索+迭代深搜

题意:给定一个1~n的排列x,每次你可以将x1~xi翻转。你需要求出将序列变为升序的最小操作次数。有多组数据。
n<=25
吃了搜索的亏,表示估价函数这玩意儿碰都没碰过,A*也是好久以前才做过的。。考试的时候打了个搜索还错了,没脸见人了。。
有两个优化。
第一个就是估价函数,这个必须加上,每次交换的时候,我们假设当前已经交换了x步,然后枚举答案,估价函数g为接下来要把当前序列变为升序的期望步数,他的映射为序列内相邻差>1的个数,那么x+g>ans则跳出。
第二个优化也加上就可以在100ms内跑完,就是我们不传递估价函数,而是记录当前做到哪一位,设为y,那么y+1…n已经放置完了,那么每一次我们暴力计算估价函数,然后也可以做到第一个剪枝,第二个剪枝就是当y=0是可以跳出,其实这个也不用特意这么写,但是我发现比起在dfs内暴力计算,好像传递的时间复杂度更高一些。
代码(跑的飞起)

#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=35;
int n,m;
int a[N],d[N],ans;
bool flag;
inline void rev(int x)
{
    fo(i,1,x/2)
    swap(a[i],a[x-i+1]);

}
inline int getlim()
{
    fo(i,1,n)a[i]=d[i];
    int ans=0;
    fd(i,n,1)
    {
        int k;
        fo(j,1,i)
        if (a[j]==i)
        {
            k=j;
            break;
        }
        if (k==i)continue;
        if (k>1)rev(k),ans++;
        rev(i),ans++;
    }
    return ans;
}
inline void dfs(int x,int y)
{
    if (x>ans)return;
    if (flag)return;
    while (a[y]==y&&y>0)y--;
    if (!y)
    {
        flag=1;
        return;
    }
    int g=0;
    fo(i,2,y+1)if (abs(a[i]-a[i-1])>1)g++;
    if (x+g>ans)return;
    fo(i,2,y)
    {
        rev(i);
        dfs(x+1,y);
        rev(i); 
    }
}
int main()
{
    //freopen("sequence.in","r",stdin);
    //freopen("sequence.out","w",stdout);
    int cas;
    scanf("%d",&cas);
    while (cas--)
    {
        scanf("%d",&n);
        ans=0;
        fo(i,1,n)
        {
            scanf("%d",&a[i]);
        }
        int y=n;
        while (a[y]==y&&y>0)y--;
        for(ans=0;ans<=2*n-2;ans++)
        {
            flag=0;dfs(0,y);
            if (flag)break;
        }
        printf("%d\n",ans);
    }
} 

你可能感兴趣的:(jzoj,搜索,启发式搜索,迭代深搜)