Editing aBook uva11212(迭代加深搜索 IDA*)

题意:  给出n( 2<=n<=9) 个乱序的数组  要求拍成升序  每次 剪切一段加上粘贴一段算一次  拍成1 2 3 4 .。。n即可     求排序次数

典型的状态空间搜索问题   初始状态为输入  结束状态为升序

分析: 因为n最大为就  排列最多为9!=362880个  虽软这个数字不是很大 但是每次剪切都可能不是一个数组 所以枚举量还要大大增加   所以肯定要优化

这里用到了迭代加深搜索: 最大次数为9   所以将次数从0开始枚举   直到首先找到一个最小的答案

迭代加深搜索其实和暴力算法没什么两样  但是重要的是找到启发函数(剪枝)  这个剪枝很重要  大大缩短时间

这题可得启发函数:   d为深度  h为不正确的数组  maxx为当前次数

可证明(见紫书)每一次操作  h最多减少三   本来是   d+h<=maxx 时可以成立  

现在有方程   3d+h>3maxx 时剪枝

启发函数就是和迭代加深搜索一起用 起到大量剪枝的作用

#include<bits/stdc++.h>
using namespace std;
#define N 9
int n,a[N];

bool judge(void)
{
    for(int i=0;i<n-1;i++)
      if(a[i]>=a[i+1])return 0;
      return 1;
}

int h(void)
{
    int cnt=0;
    for(int i=0;i<n-1;i++)
       if(a[i]+1!=a[i+1])cnt++;
       if(a[n-1]!=n)cnt++;
       return cnt;
}

bool dfs(int d,int maxx)
{
    if(3*d+h()>3*maxx)return false;
    if(judge())return true;
    int olda[N],b[N];
    memcpy(olda,a,sizeof a);
    for(int i=0;i<n;i++)
        for(int j=i;j<n;j++)
    {
        int cnt2=0;
        for(int k=0;k<n;k++)
            if(k<i||k>j)b[cnt2++]=a[k];

        for(int k=0;k<=cnt2;k++)
        {
             int cnt=0;
            for(int p=0;p<k;p++)a[cnt++]=b[p];
            for(int p=i;p<=j;p++)a[cnt++]=olda[p];
            for(int p=k;p<cnt2;p++)a[cnt++]=b[p];
            if(dfs(d+1,maxx))return 1;
            memcpy(a,olda,sizeof a);
        }
    }
    return 0;
}

int solve(void)
{
    if(judge())return 0;
    for(int maxx=1;maxx<=8;maxx++)
        if (dfs(0,maxx) ) return maxx;
}


int main()
{
    int cas=0;
    while(scanf("%d",&n),n)
    {
        for(int i=0;i<n;i++)scanf("%d",&a[i]);
        printf("Case %d: %d\n",++cas,solve());
    }
}

猜你喜欢

转载自www.cnblogs.com/bxd123/p/10410149.html
今日推荐