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; }