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

来源:互联网 发布:java选课系统实验报告 编辑:程序博客网 时间:2024/06/06 01:52

序列(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;}


1 0