[IDA*]序列(sequence)

序列(sequence)

【题目描述】
给定一个1n的排列x,每次你可以将x1xi翻转。你需要求出将序列变为
升序的最小操作次数。有多组数据。
【输入数据】
第一行一个整数t表示数据组数。
每组数据第一行一个整数n,第二行n个整数x1~xn。
【输出数据】
每组数据输出一行一个整数表示答案。
【样例输入】

1
8
6 1 3 2 4 5 7

【样例输出】

7

【数据范围】
对于100%的测试数据,t=5,n<=25。
对于测试点1,2,n=5。
对于测试点3,4,n=6。
对于测试点5,6,n=7。
对于测试点7,8,9,n=8。
对于测试点10,n=9。
对于测试点11,n=10。
对于测试点i(12<=i<=21),n=i。
对于测试点22,23,n=22。
对于测试点24,25,n=23。

Solution

考试的时候已经猜到是 IDA* 了,可是一直没想出来估价函数是啥,写了个迭代加深搜索骗了个暴力分。。。

考虑到这是一个排列,最终的状态一定是 num[i]+1==num[i+1]
而每次交换最多只会改变一组相邻元素的差值
所以对于某一个状态,相邻的元素之差大于 \(1\) 的个数就可以作为估价函数的值,因为最优解步数不小于这个。

#include<ctime>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 36;
int n;
int tmp[MAXN];
int num[MAXN];
int ans;

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

void SWAP(int l,int r){
	for(int i=l;i<=r;i++)
		tmp[i]=num[i];
	for(int i=r,j=l;i>=l;i--,j++)
		num[i]=tmp[j];
}

bool H(int x,int limit){
	int re=0;
	for(int i=1;i<n;i++){
		if(abs(num[i]-num[i+1])>1)
			re++;
	}
	return re+x<=limit;
}

void DFS(int x,int limit){
	if(x==limit+1){
		if(check())
			ans=limit;
		return;
	}
	for(int i=1;i<=n;i++){
		if(ans!=2*n+1)
			return;
		SWAP(1,i);
		if(H(x,limit))
			DFS(x+1,limit);
		SWAP(1,i);
	}
}

void solve(){
	ans=2*n+1;
	if(check()){
		puts("0");
		return;
	}
	for(int i=1;i<=2*n;i++){
		DFS(1,i);
		if(ans!=2*n+1)
			break;
	}
	printf("%d\n",ans);
}

int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	int kase;
	scanf("%d",&kase);
	while(kase--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			scanf("%d",&num[i]);
		solve();
	}
	return 0;
}
posted @ 2018-01-21 13:04  zzzc18  阅读(236)  评论(0编辑  收藏  举报