分糖果

题目描述

小明从糖果盒中随意抓一把糖果, 每次小明会取出一半的糖果分给同学们.

当糖果不能平均分配时, 小明可以选择从糖果盒中 (假设盒中糖果足够) 取出一个糖果或放回一个糖果.

小明最少需要多少次 (取出, 放回和平均分配均记一次), 能将手中糖果分至只剩一颗.

输入描述

抓取的糖果数 (< 10000000000)

输出描述

最少分至一颗糖果的次数

示例1

输入:

15

输出:

5

示例2

输入:

6

输出:

3

题解

动态规划的思路:

  • 如果是奇数个糖果, 就有两种方法: 取一个再平分;放一个再平分. 我们可以分别计算它们的结果, 再求得其中的最小值
  • 如果是偶数个糖果, 就先平均分一次
  • 使用缓存存储中间结果, 加快运算

Python

import sys

def main():
    cache = {1: 0}

    # 缓存 + 递归
    def get_minimum_times(num: int) -> int:
        if num in cache:
            return cache[num]
        if num % 2 == 0:
            # 如果是偶数个
            # 平均分一次
            times = 1 + get_minimum_times(num // 2)
            cache[num] = times
            return times
        else:
            # 如果是奇数个, 有两种方式:
            # 取一个
            times1 = 1 + get_minimum_times(num + 1)
            # 放一个
            times2 = 1 + get_minimum_times(num - 1)

            # 求它们的最小值
            min_times = min(times1, times2)
            cache[num] = min_times
            return min_times

    sweet = int(input().strip())
    times = get_minimum_times(sweet)
    print(times)

if __name__ == "__main__":
    main()

C++

#include <iostream>
#include <unordered_map>

// 缓存 + 递归
int get_minimum_times(int num, std::unordered_map<int, int>& cache) {
  const auto iter = cache.find(num);
  if (iter != cache.end()) {
    return iter->second;
  }

  if (num % 2 == 0) {
    // 如果是偶数个
    // 平均分一次
    const int times = 1 + get_minimum_times(num / 2, cache);
    cache[num] = times;
    return times;
  } else {
    // 如果是奇数个, 有两种方式:
    // 取一个
    const int times1 = 1 + get_minimum_times(num + 1, cache);
    // 放一个
    const int times2 = 1 + get_minimum_times(num - 1, cache);
            
    // 求它们的最小值
    const int min_times = std::min(times1, times2);
    cache[num] = min_times;
    return min_times;
  }
}

int main() {
  std::unordered_map<int, int> cache;
  cache[1] = 0;

  int num_candies = 0;
  std::cin >> num_candies;

  const int times = get_minimum_times(num_candies, cache);
  std::cout << times << std::endl;

  return 0;
}

Rust

use std::collections::HashMap;
use std::io::{stdin, BufRead};

fn main() {
    // 缓存 + 递归
    fn get_minimum_times(num: usize, cache: &mut HashMap<usize, usize>) -> usize {
        if let Some(value) = cache.get(&num) {
            return *value;
        }
        if num % 2 == 0 {
            // 如果是偶数个
            // 平均分一次
            let times = 1 + get_minimum_times(num / 2, cache);
            cache.insert(num, times);
            times
        } else {
            // 如果是奇数个, 有两种方式:
            // 取一个
            let times1 = 1 + get_minimum_times(num + 1, cache);
            // 放一个
            let times2 = 1 + get_minimum_times(num - 1, cache);

            // 求它们的最小值
            let min_times = times1.min(times2);
            cache.insert(num, min_times);
            min_times
        }
    }

    let mut cache = HashMap::from([(1, 0)]);
    let mut line = String::new();
    let ret = stdin().lock().read_line(&mut line);
    assert!(ret.is_ok());
    let num_candies: usize = line.trim().parse().unwrap();

    let times = get_minimum_times(num_candies, &mut cache);
    println!("{times}");
}