noip模擬賽(10.4) 序列(sequence)
阿新 • • 發佈:2018-12-30
序列(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; }