问题 I: 【组合数学】取数问题
题目描述
任给出正整数n和k(1<=n<=1,000,000 , 0<=k<=n),取数规则如下:
例如n=16,k=4
第一次取数 1 取数后的余数为16-1=15
第二次取数 2 取数后的余数为15-2=13
第三次取数 4 取数后的余数为13-4=9
第四次取数 8 取数后的余数为 9-8=1
当第五次取数时,因为余数是1,不够取,此时作如下处理
余数1+k=5,再从1开始取
第五次取数 1 取数后的余数为5-1=4
第六次取数 2 取数后的余数为4-2=2
由于第七次取数4,但余数为2,又得重新加k,2+4=6,再从1开始取
第七次取数 1 取数后的余数为 6-1=5
第八次取数 2 取数后的余数为 5-2=3
第九次取数 4 但不够取
3+4=7
第九次取数 1 取数后的余数为 7-1=6
第十次取数 2 取数后的余数为 6-2=4
第十一次取数 4 取数后的余数为 4-4=0 正好取完
由此可见,当n=16,k=4时,按上面方法11次取完
输入
两个int类型的正整数,n和k。
输出
如果正好取完,输出按照取数规则取完所需要的取数次数。如果永远不能取完时,输出”Bad Number!”。
样例输入
复制样例数据
16 4
样例输出
11
设余数为 res
按照规则, 每次剪掉的数是2 ^ 0 , 2 ^ 1 , 2 ^ 2 , 2 ^ 3 … 2 ^ i , 这个2 ^( i + 1 )> res ,故而不能再减了, 前 i 项我们可以直接用等比做一下 , 我是提前存起来的,然后后面直接相减 , 但是这样的话, 会有一个问题, 我们要循环多少次,才能最后按照规则得出0 呢, 这个还真不知道,
不过可以发现我们先找出一个规律, 根据等比数列公式, 前i + 1项和 为2 ^(i + 1) - 1 . 比2 ^ (i + 1) 少了 1 , res <= 2 ^ (i + 1) - 1. 我们可以发现res 不超过一半大小, 也就是如果res >= 2 ^ (i + 1) , 我们可以直接减掉2 ^ (i + 1) , 也就保证了res < 2 ^ (i + 1) , =额, 还真有点说不清,
看样例14 6 第一次拿 1 , 2 ,4 ,剩下 7, 下面不够拿8的了, 然后再加上k重新拿, 一次模拟下去可以发现 ,每次剩下的余数真的不超过n的一半大小,那么再加上k,就是不超过k + n / 2, 最后为0 . 会去拿多少次? 每次相当于取走一半, 不回超过k + n / 2 次, 枚举这些遍就行了,如果最后是0 , 可以拿完, 同时记录一下拿了多少次, 如果不是,就是Bad
#include <iostream>
#include <cstdio>
using namespace std;
int b[31] , n , k ;
int main()
{
for(int i = 1 , j = 1 ;i <= 30 ;j *= 2 , i ++)
b[i] = b[i - 1] + j ;
scanf("%d%d" , &n , &k) ;
int total = 0 , res = n ;
for(int sum = 0 ;sum <= k + n / 2 ;sum ++ , res += k)
{
for(int i = 1 ;i <= 30 ;i ++)
if(b[i] > res)
{
res -= b[i - 1] ;
total += i - 1;
break ;
}
if(!res) break ;
}
if(res) puts("Bad Number!") ;
else printf("%d\n" , total) ;
return 0 ;
}