砝码问题

        最近一段时间都在被虐,是时候总结一下遇到的问题,准备新一轮被虐。

        在这里总结一下与砝码有关的问题,砝码只是个代名词,算法可以运用到其他的场景下才是最重要的。

 

一、砝码表示范围的问题

        所谓砝码的表示范围,即在一定范围的重量需要使用多少个砝码的问题。这类问题需要数学知识,在此向数学势力低头。

首先理解题意,一般问题会告诉我们砝码是否必须同边。(这里说到的被称物体都放在同一边)

如果必须同边,那么每个砝码的状态只有两种,要么放,要么不放。到这里,是不是觉得有点熟悉有陌生的感觉?如果把砝码看成电路的开关呢?想不到吧,这个问题纯粹是进制问题了。进制问题啊!!!为什么我对着电脑看来这么久都没想到

0

0

0

0

0

0

0

1

1

1

1

1

1

1

      如上图的7位二进制可以表示的数字范围是:0~127(27-1)。所以七个不同的砝码的可以表示的范围也就是[0,127]

 

        如果不是必须同边,那么每个砝码有三种状态,与重物异侧,与重物同侧,不放。有上面可知,这也是个进制问题,只是严格意义上不能称之为三进制,因为这种进制比较特殊。如下图:

-1

-1

-1

-1

0

0

0

0

1

1

1

1

       因为砝码放在重物同侧相当于减法,所以4位该进制表示的范围是:-40~40。40是这么来的:(34-1)/2,这里需要除以一个二,因为这个表示法是以0对称的。

已知砝码个数,求表示范围:

核心代码:

int ans = (pow(3,n)-1)/2;

在这里建议手动乘方:

for(int i=1;i<=n;i++)

{r = 3*r;}

ans = (r-1)/2;

 

    已至范围求砝码数:    核心代码:

int ans = log(2*n+1,3);

    或:

int i = 1; int m = 1; int k = 3; 
while(m<n){k = k*3;m = (k-1)/2;i++; } //i即为答案

二、已知砝码重量和个数,求砝码可以表示的重量。

这个问题还是逃不过动态规划了,也就是俗称dp数组,最重要是要找到递推公式。讲到递推公式,肯定离不开数学,所有再次向数学势力低头。

假设有n种砝码,每种有m1…mn,问可以称出的重量。在这里最重要是找到每个情况与前一种情况的关系。当砝码规格只有一种时,个数为m1个:f(1)=m1 。当砝码规格有两种,个数为m1 m2 f(2)=f(1)+x。关键在于如何确定x

核心代码:

int n;  //有n种砝码

int w[],m[],d[];    //w[]表示每种砝码的重量,m[]表示每种砝码的个数,d[]表示可以表示的重量,d[i]=1表示i可表示。

int sum;    //记录所有砝码用完可表示的最大重量。

int fmaxsum;    //用来记录每种情况可表示的最大重量。

d[0]=1; //注意要算上0.

for(int i=1;i<m[0];i++){

d[i*w[0]]=1;    //把第一种砝码的所有情况进行标记。第一种情况需要单独列出是因为无法递推,例如数列中的a0。

}

fmaxsum = w[0]*m[0];    //记录当前可表示的最大值。

int i = 1;  //由第二种情况开始递推。

int c;  //c起试探作用。试探在未加第i+1种砝码时可能称到的情况。

int nw; //nw用来记录新的重量。

while (i < n)

    {

        for (j = 1; j <=m[i]; j++)  //第i种砝码的个数最多为m[i]

        {

            for(c=0;c<=fmaxsum; c++)    //c用来试探。

            {

                nw = c + j*w[i];

if (flag[c]==1&&flag[nw]==0)    //只要在未加当前砝码时,可以表示出来,那新的重量即可表示

                {

                    flag[nw] = 1;

                }

            }

        }

    fmaxsum = fmaxsum + m[i] * w[i];    //更新砝码的可称的最大重量

        i++;

}

计算数量遍历一次数组d即可。

三、已知砝码重量和个数,算每种重量的砝码表示需要使用的最少砝码数。

同样时一个递推过程,如有砝码1,2,4,8各1个,则先把无需递推即:

d[1]=d[2]=d[4]=d[8]=1;

然后得到递推公式d[i]=d[i-w[j]]+1;在这里需要得到最少这种最佳情况。

核心代码:

int w[4]={1,2,4,8}; //注意w[]要按升序排列。

d[0]=0; //注意d[0]为0.

for(int i=1;i<=m;i++){

    d[i]=d[i-w[0]]+1;

    for(int j=1;j<4;j++){

        d[i]=min(d[i-w[j]]+1,d[i]); //每种情况进行比较,得到最优。

    }

}

谢谢大家,有遗漏的我会继续补充。我先去准备新一轮被虐啦!在这里再次向数学势力低头。

猜你喜欢

转载自blog.csdn.net/qq_39458250/article/details/86701629