POJ3460 Booksort IDA*算法

22 篇文章 0 订阅

题目描述
给定n本书,编号为1-n。

在初始状态下,书是任意排列的。

在每一次操作中,可以抽取其中连续的一段,再把这段插入到其他某个位置。

我们的目标状态是把书按照1-n的顺序依次排列。

求最少需要多少次操作。
输入格式
第一行包含整数T,表示共有T组测试数据。

每组数据包含两行,第一行为整数n,表示书的数量。

第二行为n个整数,表示1-n的一种任意排列。

同行数之间用空格隔开。

输出格式
每组数据输出一个最少操作次数。

如果最少操作次数大于或等于5次,则输出”5 or more”。

每个结果占一行。

数据范围
1 ≤ n ≤ 15

样例

输入样例:
3
6
1 3 4 6 2 5
5
5 4 3 2 1
10
6 8 5 3 4 7 2 9 1 10
输出样例:
2
3
5 or more

思路
        根据题目要求,我们只需要考虑在4次操作以内是否能实现目标,也就是我们只需要考虑搜索树的前4层。本层的搜索树的规模能够达到560^4, 超时。
        方案一:采用双向BES.从起始状态、目标状态开始各搜索2步,看能否到达相同的状态进行衔接,复杂度降低为560^2。
        方案二:在目标状态下,第i本书后边应该是第i+1本书,我们称i+1是i的正确后继。每次修改最多可以修改3个节点,如果序列中顺序不对的地方有tot个, 那么我们最少可以修改tot / 3 次得到最终结果。所以我们设置估计函数为f(n) = tot / 3 上取整。 我们采用迭代加深的方法,从1~4依次限制搜索深度,然后从起始状态出发DFS。DFS时,在每个状态下直接枚举抽取哪一段、移动到更靠后的哪个位置,沿着该分支深入即可。

代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 20;
int q[N];
int stp[5][N];
int n;
int f(){
	int ans = 0;
	for(int i = 0; i < n - 1; i++){
		if(q[i + 1] != q[i] + 1) 
			ans++; 
	}
	return (ans + 2) /3;
}

bool check(){
	for(int i = 0; i < n; i++){
		if(q[i] != i + 1) 
			return false;
	}
	return true;
}

bool dfs(int depth, int maxx){
	if(depth + f() > maxx) return false;
	if(check()) return true;
	
	for(int l = 0; l < n; l++)
		for(int r = l; r < n; r++){
			for(int k = r + 1; k < n; k++){
				memcpy(stp[depth], q, sizeof q);
				int y, x;
				for(x = r + 1, y = l; x <= k; x++, y++) q[y] = stp[depth][x];
				for(x = l; x <= r; x++, y++ ) q[y] = stp[depth][x];
				if(dfs(depth + 1, maxx)) return true;
				memcpy(q, stp[depth], sizeof q);
			}
		}
	return false;
} 
int main(){
	int t;
	cin >> t;
	while(t--){
		cin >> n;
		for(int i = 0; i < n; i++) cin >> q[i];
		int depth = 0;
		while(depth < 5 && !dfs(0, depth)) depth++;
		if(depth >= 5) cout << "5 or more" << endl;
		else cout << depth << endl;
	}
	return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值