小议CSDN周赛57期 - 凑数

本期周赛几乎忘记参加,在最后几分钟的时候上来看了看。那些选择判断一通乱选,填空题也已经被吐槽得差不多了,这里不多说,只说我对第一道编程题的看法(吐槽)。因为 C 站的机制是,即使它错了,它也不会承认(印象里只承认且改过一次),再争辩只会把你拉黑,甚至限流也有可能,所以只能把我的观点放在博客里,不再专门去反馈了。


先来看题目:

给定一组n个正整数,要求每次选其中一个数乘以或除以一个素数(称为一次凑数),问至少需要凑数多少次可以把所有的数都凑成相等。

输入描述:

第一行输入n个正整数(0<n<=1000000,其中每个数都小于1000000)。

输出描述:

可以把所有的数都凑成相等需要至少进行的凑数次数t(t>=0)。

输入样例:

5 20

输出样例:

2

我的观点是:这是道好题——好题在我这里的定义是,大部分人的解读是错的。而且至少目前来看,还没发现有人说对了。甚至我有九成的把握,此题测试用例的“标准答案”也是错的,剩下一成留给出题人来“深度解读”题意。

我认为此题关于“凑数”的定义是没有歧义的:

  1. 每次选其中一个数
  2. 将其乘以除以一个素数(质数)

同时满足上面两点即称为“一次凑数”。而将所有数字通过凑数变成相等的目标数字,却是没有规定的。

所以根据上述规则,对于给出的样例的两个数字 5 和 20 来说,有三种最优的方式(两次凑数)将其变成相等。

  • 5*2*2=20
  • 20\div 2\div2=5
  • 5*2=10 , 20\div2=10

只有两个数字时,找出这样的目标数字并不难——肉眼可见,前两种方法分别是最小公倍数最大公约数。但能不能把这种规律扩展到三个数字以上呢?相信很多人的第一反应也是这样做的(通过测试用例的答案来看,感觉出题人可能也是这样想的)。但是很遗憾,由于“凑数”的定义是“其中一个数“”可以“乘以或除以”,并没有规定所有数都只能乘以或除以,所以存在第三种情况,即当某一部分的数字乘以一个素数,另一部分的数字除以一个素数的时候,也就是说,将所有数字变成一个最小公倍数和最大公约数之间的中间数字的时候,总的操作数更少。

举个简单的例子:2, 4, 8 。这三个数字的最小公倍数是 8, 最大公约数是 2,但是如果将三个数字都变成 8 或 2,都需要 3 次凑数:

  • 2*2*2=8,4*2=8
  • 8\div2\div2=2,4\div2=2

但是如果把三个数字都变成 4,只需要 2 次凑数:

2*2=4,8\div2=4

所以,我们不能完全通过找最小公倍数或最大公约数,找到最优的凑数方案。

那该怎么做呢?

我们知道,任何正整数都可以进行质因数分解,写成 p_{1}^{x}*p_{2}^{y}*...*p_{n}^{z} 的形式。其中 p_{n} 表示所有素数,(x,y,z)\geq 0 。

而将所有整数都变成一个相等的目标数字,等价于将所有整数的质因数排列形式变成一致。其中,整数里原本没有的质数,就要加上(乘以),原本多出来的质数,就要减少(除以),最终使得所有数字包含的每个质数的个数都各自相同。而相信你也看出来了,这里的“加上”和“减少”操作,其实就是本题所定义的“凑数”。

用公式来说明,假如有三个整数 A,B,C,分别可以写成:

  • A = p_{1}^{x_{1}}*p_{2}^{y_{1}}*p_{3}^{z_{1}}
  • B = p_{1}^{x_{2}}*p_{2}^{y_{2}}*p_{3}^{z_{2}}
  • C = p_{1}^{x_{3}}*p_{2}^{y_{3}}*p_{3}^{z_{3}}

而我们想要凑成的最终数字,应该是:

Target = p_{1}^{x_{n}}*p_{2}^{y_{n}}*p_{3}^{z_{n}}

其中,x_{1},x_{2},x_{3} 各自通过加减,统统变成 x_{n}y_{1},y_{2},y_{3} 各自通过加减,统统变成 y_{n}z_{1},z_{2},z_{3} 各自通过加减,统统变成 z_{n} 。不难看出,本题所要找的“凑数次数”,就是上面这些质数原本的个数变成最终个数,所需要的加或减的次数之和。而要使这个“次数之和”最小,每个质数原本的个数到最终个数的距离之和就必须最小。

所以,问题的关键变成了如何找出每个质数的“最终个数”。

相信有点数学基础的同学,很容易就能发现:在一群数中间找到一个数,使得所有数字到这个数字的距离之和最小,那么这个数必然是这群数字的中位数。——实际上,这也是中位数用处最广的特性之一。

到此,解题方法呼之欲出了。只需下面三步:

  1. 将所有整数进行质因数分解,找出所有质数的个数
  2. 将这些质数的个数分别进行排序,找到每个质数个数的中位数(没有该质数的整数相应的个数记为 0,参与排序)
  3. 累加每个质数到其中位数的个数之差。

相关代码放在某个地方了,这里就不重复了 :D

但是很遗憾,虽然我自信这种做法是正确的,但却通不过 C 站的测试。——这也是为什么我说九成认定 C 站的答案错了。

以彼之矛攻彼之盾,下面我们用 C 站本题自己的用例来证明它是错的。

本题有一组相对数量较少的用例如下:

arr = [934906, 869814, 593826, 642589, 904024, 572674, 718422, 812732, 873256, 582947]

标准答案是 38。也就是说 C 站认为需要至少 38 次凑数才能将所有数字变成相等。

虽然我不知道 C 站认为的最终相等的数字是多少,但我知道将所有数字都变成 1,是需要 38 次的,所以可以拿来进行分析和比较。具体如下:

  • 934906\div 2\div7\div43\div1553=1
  • 869814 \div2\div3\div3\div11\div23\div191=1
  • 593826 \div2\div3\div19\div5209=1
  • 642589\div313\div2053=1
  • 904024 \div2\div2\div2\div11\div10273=1
  • 572674 \div 2\div43\div6659=1
  • 718422 \div 2\div3\div119737=1
  • 812732 \div 2\div2\div203183=1
  • 873256 \div2\div2\div2\div17\div6421=1
  • 582947 \div 17\div53\div647=1

虽然本例有点变态(出现了6位质数),但万变不离其宗,用我们前面介绍的方法进行分析,步骤如下:

1、将所有整数进行质因数分解,用 p_{1}^{x}*p_{2}^{y}*...*p_{n}^{z} 形式表示:

  • 934906= 2^{1}*7^{1}*43^{1}*1553^{1}
  • 869814=2^{1}*3^{2}*11^{1}*23^{1}*191^{1}
  • 593826 =2^{1}*3^{1}*19^{1}*5209^{1}
  • 642589=313^{1}*2053^{1}
  • 904024 =2^{3}*11^{1}*10273^{1}
  • 572674=2^{1}*43^{1}*6659^{1}
  • 718422=2^{1}*3^{1}*119737^{1}
  • 812732=2^{2}*203183^{1}
  • 873256=2^{3}*17^{1}*6421^{1}
  • 582947 =17^{1}*53^{1}*647^{1}

2、将所有出现过的质数的个数单独进行排序,不足整数个数的用 0 补齐(说明某些整数自己没有这个质数),并找出中位数(由于本例整数个数为10,偶数,所以中位数可以取左边,也可以取右边,这里便于计算,统一取左边):

  • 2:[0,0,1,1,1,1,1,2,3,3],中位数为 1
  • 3:[0,0,0,0,0,0,0,1,1,2],中位数为 0
  • 7:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 11:[0,0,0,0,0,0,0,0,1,1],中位数为 0
  • 17:[0,0,0,0,0,0,0,0,1,1],中位数为 0
  • 19:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 23:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 43:[0,0,0,0,0,0,0,0,1,1],中位数为 0
  • 53:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 191:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 313:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 647:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 1553:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 2053:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 5209:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 6421:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 6659:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 10273:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 119737:[0,0,0,0,0,0,0,0,0,1],中位数为 0
  • 203183:[0,0,0,0,0,0,0,0,0,1],中位数为 0

可以看出,绝大多数质数在所有整数中出现的个数都是1,所以本例的数字大概率是随机生成,并不是一个好的示例,但判断的逻辑是一样的。这一步也提示了最优的目标数字是 2(只有 2 的个数中位数是1,其余质数个数中位数都是 0)。

3、将每个质数个数列表中的其他数字向其中位数“靠齐”。以 2 和 3 为例:

  • 2:[0,0,1,1,1,1,1,2,3,3],所有数字变成 1,需要进行 7 次凑数——两个 0 变成两个 1需要加上两个1;2、3、3变成3个1,需要分别减去1、2、2
  • 3:[0,0,0,0,0,0,0,1,1,2],所有数字变成 0,需要进行 4 次凑数——1、1、2全变成 0需要分别减去1、1、2

其余质数依次类推。最后再将所有次数累加,得到较之“标准答案”更优的答案——32。所以,我认为的正确答案应该是 32,也就是将所有数字变成 2,仅需要 32 次凑数:

  • 934906\div7\div43\div1553=2
  • 869814 \div3\div3\div11\div23\div191=2
  • 593826 \div3\div19\div5209=2
  • 642589\div313\div2053*2=2
  • 904024 \div2\div2\div11\div10273=2
  • 572674 \div43\div6659=2
  • 718422 \div3\div119737=2
  • 812732 \div2\div203183=2
  • 873256 \div2\div2\div17\div6421=2
  • 582947 \div 17\div53\div647*2=2

其实,通过肉眼就可以发现,十个整数中有八个偶数,只有两个奇数,很显然将两个奇数乘以 2,要比将八个偶数除以 2,次数更少。


至于如何通过 C 站本题的测试,我也早已有了答案,只是对于这种题意和答案不符的情况,还是不吐(槽)不快。

好了,是非对错,有缘读到这里的读者自己判断吧。

也欢迎有人提出反对意见,毕竟胜负终归浮云,唯有真理越辩越明。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请叫我问哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值