POJ 3460 Booksort IDA*

题意: 给你一个长度为N(N<=15)的序列,让你通过最少的操作次数将这个序列变成单调递增的。允许的操作为:选择一段连续的区间,将其从原序列中取出,然后将其插入到任意位置。

思路:首先我们可以发现,最多的操作次数为N次,即每次我们只将一个数字放到正确的位置。但是最少的操作次数该如何计算呢?

            因为,我们注意到对于不同的序列,其操作的方法没有明显的规律性,这样我们就要用搜索来完成这个问题,同时在前面也提到了操作次数的上界,那IDA*算法就是个很好的选择。

           IDA*算法的关键就是设计启发式函数h,即当前状态到最终状态的下界。这里,我们用h来表示后继不正确的数字个数,可以得到性质:每次操作,h的值最多减少3。那么,一个很自然的剪枝就是:3d+h > 3maxd。即,如果对于当前状态的h,剩下的(maxd - d)次操作都不能将其变成0,则剪枝。

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int MAX = 20;

int T,N;
int a[20];
int sa[20];

bool judge()
{
    for(int i = 0; i < N; ++i)
        if(sa[i] != a[i]) return false;
    return true;
}

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

bool dfs(int d, int maxd)
{
    if(judge()) return true;
    if(3 * d + h() > 3 * maxd) return false;
    int olda[20];
    int b[20],tot;
    memcpy(olda,a,sizeof(a));
    for(int i = 0; i < N; ++i){
        for(int j = i; j < N; ++j){
            tot = 0;
            for(int k = i; k <= j; ++k)
                b[tot++] = olda[k];
            for(int k = 0; k < N; ++k){
                memcpy(a,olda,sizeof(olda));
                if(k < i){//在k位置的前面插入
                    for(int u = 0; u < i - k; ++u)
                        a[j - u] = a[i - u - 1];
                    for(int u = 0; u < tot; ++u)
                        a[k + u] = b[u];
                    if(dfs(d+1,maxd)) return true;
                }
                else if(k > j){//在k位置的后面插入
                    for(int u = 0; u < k - j; ++u)
                        a[i + u] = a[j + u + 1];
                    for(int u = 0 ; u < tot; ++u)
                        a[i + k - j + u] = b[u];
                    if(dfs(d+1,maxd)) return true;
                }
            }
        }
    }
    return false;
}

int IDAstar()
{
    if(judge()) return 0;
    for(int i = 1; i <= 4; ++i)
        if(dfs(0,i)) return i;
    return 5;
}

int main(void)
{
    //freopen("input.txt","r",stdin);
    scanf("%d", &T);
    while(T--){
        scanf("%d",&N);
        for(int i = 0; i < N; ++i){
            scanf("%d",&a[i]);
            sa[i] = a[i];
        }
        sort(sa,sa+N);
        int ans = IDAstar();
        if(ans <= 4)
            printf("%d\n",ans);
        else
            puts("5 or more");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值