题意:
给出两个数列a和b(1<=n<=9),通过以下操作使a变成b,求最小操作数。
首先可以交换数列中任意两个数的位置。通过若干次这样的操作得到c。
若c[i]>a[i],则对a[i]乘2或者加1;若c[i]<a[i],则对a[i]除以2或者减1,如果是奇数,则不能除以2。
思路:
考虑到这里的n的范围很小,可以根据全排列来表示出所有c[i]可能的情况。
再算出从a到c所用的最小操作数,可以用判环的方法,也可以直接交换。遍历下去如果有不符合自己位置的,就向后找到这个位置该放的那个数,进行交换,计数即可。
考虑如何从一个数变成另一个数,如果c[i]小于a[i]的时候就进行除法或减法运算,尽量除,是没有问题的,但是如果c[i]大于a[i]进行乘法或加法运算的时候尽量乘的策略就有问题了,比如2->7,如果用尽量乘的策略,2×2=4,4+3=7,这里用这个策略要4步操作,但实际上,如果这样,2+1=3,3×2=6,6+1=7则只要3步操作,之所以会这样是因为*2如果超过了想要的值,之后一直往上加就会造成操作数变多。所以可以想到a[i]->c[i]也相当于c[i]->a[i],所以就只用除法和减法运算即可。
代码:
int a[15],b[15],p[15],tp[15];
int cost[15][15];
int n;
int trans()
{
for(int i=1;i<=n;i++)
tp[i]=p[i];
int ans=0;
for(int i=1;i<=n;i++)
{
if(tp[i]!=i)
{
for(int j=i+1;j<=n;j++)
{
if(tp[j]==i)
{
swap(tp[i],tp[j]);
break;
}
}
ans++;
}
}
return ans;
}
int cal(int x,int y)//x->y
{
int ans=0;
if(x==y)return 0;
if(x<y)swap(x,y);
while(x>y)
{
if(x%2==1)
{
if(x-1>=y){x--;ans++;}
}
else
{
if(x/2>=y){x/=2;ans++;}
else {ans+=x-y;x=y;break;}
}
}
return ans;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
p[i]=i;
}
for(int i=1;i<=n;i++)
scanf("%d",&b[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)//预处理出每个a[i]转化为b[j]需要几次操作
cost[i][j]=cal(a[i],b[j]);
int ans=INF;
do
{
int temp=0;
temp+=trans();
for(int i=1;i<=n;i++)
temp+=cost[p[i]][i];
ans=min(ans,temp);
}while(next_permutation(p+1,p+n+1));
printf("%d\n",ans);
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!