题目大意:给n(n<8)个数放置在n个位置,小的数可以移动至大的数上面,反之不行。输出使得其有序最少需要的移动次数。
最多只有7个数,首先可以把输入的数离散化处理一下。
对于每一个数,考虑用三位二进制数来表示其位置。那么对于n个数,有2^(3*n)种状态。
假设a[i]的位置为pos[a[i]],可用pos[a[1]]*8^a[1]+pos[a[2]]*8^a[2]+pos[a[3]]*8^a[3]+……+pos[a[n]]*8^a[n]来表示每一个状态s。
如何根据当前状态确定某个数a[i]的位置?
设当前状态为s,则(s/(8^a[i]))%8即为当前状态下,数a[i]所在的位置pos[a[i]]。
设定了状态,并且知道了如何从某一状态下确定某个数的位置,就可以通过预处理,从目标位置开始bfs,得到每一个状态到达目标位置所需步数。然后O(1)回答每个询问了。
BFS时,枚举一个数,其可以向左或向右放置。由于放置时只能放在比它大的数上,因此可以去判断一下比它小的数是不是在要放置的位置上,如果在,则不放置。相当于一个剪枝。
#include<bits/stdc++.h> using namespace std; #define maxn 2100000 struct P{ int x,id; }p[7]; int ans[maxn]; bool vis[8]; queue<int> Q; int getnum(int n) { int s=0; for(int i=0;i<n;++i) s+=((i+1)*(1<<(3*i))); return s; } bool check(int a,int p,int s) { for(int i=0;i<a;++i) { int pos=(s>>(3*i))%8; if(pos==p) return 0; } return 1; } void bfs(int n) { int i,s=getnum(n); ans[s]=0; Q.push(s); while(!Q.empty()) { s=Q.front();Q.pop(); memset(vis,0,sizeof(vis)); for(i=0;i<n;++i) { int pos=(s>>(3*i))%8; if(vis[pos]) continue; vis[pos]=1; if(pos>1&&!vis[pos-1]&&check(i,pos-1,s)){ int tem=s-(1<<(3*i)); if(ans[tem]==-1) { Q.push(tem); ans[tem]=ans[s]+1; } } if(pos<n&&!vis[pos+1]&&check(i,pos+1,s)) { int tem=s+(1<<(3*i)); if(ans[tem]==-1) { Q.push(tem); ans[tem]=ans[s]+1; } } } } } bool cmp1(P a,P b) {return a.x<b.x;} bool cmp2(P a,P b) {return a.id<b.id;} int main() { int T,i,n; memset(ans,-1,sizeof(ans)); for(i=1;i<=7;++i) bfs(i); scanf("%d",&T); while(T--) { scanf("%d",&n); for(i=0;i<n;++i) {scanf("%d",&p[i].x);p[i].id=i;} sort(p,p+n,cmp1); for(i=0;i<n;++i) p[i].x=i; sort(p,p+n,cmp2); int s=0; for(i=0;i<n;++i) s+=((i+1)*(1<<(3*p[i].x))); printf("%d\n",ans[s]); } return 0; }