题意:

给出两个数列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 协议 ,转载请注明出处!

LOJ526 最大独立集 Previous
CodeForces842D 01Trie Next