題目大意:給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;
}