您的位置:首页 > 产品设计 > UI/UE

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

2016-10-05 20:22 218 查看
序列(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
,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息