2824: [AHOI2012]铁盘整理
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 105 Solved: 44
[ Submit][ Status]
Description
在训练中,一些臂力训练器材是少不了的,小龙在练习的时候发现举重器械上的铁盘放置的非常混乱,并没有按照从轻到重的顺序摆放,这样非常不利于循序渐进的锻炼。他打算利用一个非常省力气的办法来整理这些铁盘,即每次都拿起最上面的若干个圆盘并利用器械的力量上下翻转,这样翻转若干次以后,铁盘将会按照从小到大的顺序排列好。那么你能不能帮小龙确定,最少翻转几次就可以使铁盘按从小到大排序呢?
例如:下面的铁盘经过如图2.1所示的以下几个步骤的翻转后变为从小到大排列。
Input
共两行,第一行为铁盘个数N(1≤N≤50)。第二行为N个不同的正整数(中间用空格分开),分别为从上到下的铁盘的半径 R(1≤R≤100)
Output
一个正整数,表示使铁盘按从小到大有序需要的最少翻转次数。
Sample Input
5
2 4 3 5 1
2 4 3 5 1
Sample Output
5
【题解】因为N<=50 , 所以我们可以用搜索;当然我觉得加一点优化会更好。
我们可以设一个估价函数,cont 记录当前状态转到目标状态最优情况下的次数,(<=(实际距离))
可以这样设:因为如果当前状态下相邻 并不是目标状态 相邻 的两个数,需要>=1的操作来分开;
所以就可以这样比较;
这样剪枝优化就可以过了。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
int ans,n,a[58],w[58];
void solve(int x,int tot)
{
if(!tot && a[1]==1){ans=min(ans,x);return;}
for(int i=2,t,cont;i<=n;i++)
{
cont=tot;
if(i<n)cont-=(abs(a[i]-a[i+1])!=1)-(abs(a[1]-a[i+1])!=1);
if(x+cont<ans)
{
t=i/2;
for(int j=1;j<=t;j++)swap(a[j],a[i-j+1]);
solve(x+1,cont);
for(int j=1;j<=t;j++)swap(a[j],a[i-j+1]);
}
}
}
inline bool cmp(int x,int y){return a[x]<a[y];}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);w[i]=i;
}
sort(w+1,w+1+n,cmp);
for(int i=1;i<=n;i++)a[w[i]]=i;
int tot=0;ans=2*n;
for(int i=2;i<=n;i++)tot+=abs(a[i]-a[i-1])!=1;
solve(0,tot);
printf("%d\n",ans);
return 0;
}