P2534 [AHOI2012]铁盘整理

Captain_Paul

2018-09-13 16:24:32

Personal

题意:一个序列,每次选择一个前缀反转,要使得从小到大排序,问最少反转几次 ------------ $n<=50,a[i]<=100$,这个范围应该是一个搜索题 直接爆搜能得到$40-60$分 可以用$IDA*$来剪枝 可以发现$ans$的上界是$2(n-1)$ 用一个变量$tot$来表示当前序列中应该相邻但并不相邻的数的个数 如果当前操作次数加上$tot$大于$ans$就直接$return$ 如果$tot$为$0$且$a[1]==1$,就更新答案 然后照常$dfs$就好了 ``` #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; const int N=55; int n,a[N],num[N<<1],ans,tot; void dfs(int sum) { if (sum+tot>=ans) return; if (!tot&&a[1]==1) {ans=min(ans,sum); return;} for (int i=2;i<=n;i++) { if (i!=n) tot+=(abs(a[i+1]-a[1])!=1)-(abs(a[i+1]-a[i])!=1); for (int j=1;2*j<=i;j++) swap(a[j],a[i-j+1]); dfs(sum+1); for (int j=1;2*j<=i;j++) swap(a[j],a[i-j+1]); if (i!=n) tot-=(abs(a[i+1]-a[1])!=1)-(abs(a[i+1]-a[i])!=1); } } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]),++num[a[i]]; for (int i=1;i<=100;i++) num[i]+=num[i-1]; for (int i=1;i<=n;i++) a[i]=num[a[i]]; ans=2*n-2; for (int i=2;i<=n;i++) if (abs(a[i]-a[i-1])!=1) ++tot; dfs(0); printf("%d\n",ans); return 0; } ```