noip模拟赛(10.4) 序列(sequence)

序列(sequence)

【题目描述】

给定一个1~n的排列x,每次你可以将x1~xi翻转。你需要求出将序列变为升序的最小操作次数。有多组数据。

【输入数据】

第一行一个整数t表示数据组数。

每组数据第一行一个整数n,第二行n个整数x1~xn。

【输出数据】

      每组数据输出一行一个整数表示答案。

【样例输入】

1

8

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<=25),n=i。

【题解】

正解:启发式搜素+迭代

即,在暴力的迭代搜索基础上,加上估价函数。

估价函数的映射规则是这样的:因为每次翻转后区间内相邻的数字出现了变化 的都是1和第i个数,当第i个数与相邻的后一个数之间差值正好为1时,这次交换导致求解的期望次数一定会+1,由此得到当目前的步数+期望步数>迭代限制时,剪枝。

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
using namespace std;
const int N=50;
inline int read()
{
	int x=0,c=getchar(),f=1;
	while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
	while(c>47&&c<58)x=x*10+c-48,c=getchar();
	return x*f;
}
bool flag;
int n,a[N],m;
inline void rev(int i)
{
	for(int j=1;j<=(i>>1);j++)
		swap(a[j],a[i-j+1]);
}
inline void dfs(int i,int k)
{
	int j,l;
	if(i+k>m)return;
	for(j=1;j<=n;j++)
		if(a[j]^j)
			break;
	if(j>n){
		flag=1;
		return;
	}
	for(j=2;j<=n;j++){
		l=k+(j<n && abs(a[j]-a[j+1])==1)-(j<n && abs(a[1]-a[j+1])==1);
		rev(j);
		dfs(i+1,l);
		rev(j);
		if(flag)
			return;
	}
}
int main()
{
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	int i,k,T=read();
	while(T--){
		n=read();
		for(i=1;i<=n;i++)
			a[i]=read();
		for(i=1,k=0;i<=n-1;i++)
			if(abs(a[i]-a[i+1])>1)
				k++;
		for(m=0;;m++){
			flag=0;
			dfs(0,k);
			if(flag)
				break;
		}
		printf("%d\n",m);
	}
	fclose(stdin);
	fclose(stdout);
	return 0;
}



 

posted @ 2016-10-05 20:22  keshuqi  阅读(312)  评论(0编辑  收藏  举报