分石子(思维题 & 二进制)

本文探讨了将n个石子分成k堆的算法,确保[1,n]间任一数可通过若干堆组合而成。通过分析石子堆数量与二进制位数的关系,提供了一种高效解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

有n个石子,将它分成k堆,对于[1,n]的任意一个数,都能用某几堆石子组成(每一堆只能用一次),求k的最小值。

Input

多组样例输入输出,输入n(1=<n<=1e18)

Output

每个测试样例输出k,占一行,k的含义如题目所述

Sample Input 1

6
2
1
Sample Output 1

3
2
1
Hint

分成的任意两个石子堆中石子个数可以是相同的。

以前这个题目不理解,但是后来有人问这个,我思考了一下然后来补充一下:所有数字都是可以用二进制表示的,所以,我们假设一个数字n,用二进制表示为k位。那么小于等于n的数,位数一定小于等于k。并且n 大于一共 k-1 位二进制数(每一位都是1)。那么,我们就不断的将这个n进行划分,每一部分(除最后一部分外)都是2的幂的形式,也就是,先把一个 k -1 位的二进制数的每一位都填上一,直到最后第k位没法填充为止,这样就正好分成了k堆。那么,从1到n。不管是哪个数,用二进制表示出来,取相应位置的那一堆石子就可以了。

比如:6 = 110,那么我们先填最低位,就是001, 然后剩了5,再填第二位,也就是010,这样剩下3,要填最高位,但是最高位是4, 3 < 4,填不上,那么3就自己一组,011.这样就直接算一个数的二进制位数就可以了。

AC代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#define ll long long
using namespace std;

int main()
{
    ll n;
    while(~scanf("%lld", &n))
    {
        ll ans = 0;
        while(n)
        {
            n >>= 1;
            ans++;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

### 关于石子的动态规划解决方案 #### 问描述 在一个圆形操场的四周摆放着 $ N $ 堆石子,每堆石子有一定数量。目标是通过合并这些石子形成一堆,其中每次只能合并相邻的两堆石子,合并的代价为这两堆石子的数量之和。最终需要计算出将 $ N $ 堆石子合并成一堆的最大得和最小得。 --- #### 动态规划解思路 此问是典型的区间动态规划问,可以通过定义状态、设计状态转移方程以及初始化来求解。 1. **状态定义** - 设 `minCost[i][j]` 表示从第 $ i $ 堆石子到第 $ j $ 堆石子合并成一堆所需的最小代价。 - 设 `maxCost[i][j]` 表示从第 $ i $ 堆石子到第 $ j $ 堆石子合并成一堆所需的最大代价。 - 辅助数组 `sum[i][j]` 表示从第 $ i $ 堆石子到第 $ j $ 堆石子的总石子数。 2. **状态转移方程** 对于任意一段区间 $[i, j]$,假设最后一次合并在位置 $ k $ 处发生,则有: \[ minCost[i][j] = \min_{i \leq k &lt; j} (minCost[i][k] + minCost[k+1][j] + sum[i][j]) \] \[ maxCost[i][j] = \max_{i \leq k &lt; j} (maxCost[i][k] + maxCost[k+1][j] + sum[i][j]) \] 3. **初始条件** 当区间的长度为 1 时,即只有一堆石子时,无需任何操作,因此: \[ minCost[i][i] = 0,\quad maxCost[i][i] = 0 \] 4. **辅助函数** 计算区间 $[i, j]$ 的总石子数: \[ sum[i][j] = \text{prefixSum}[j] - \text{prefixSum}[i-1],\quad \text{(if } i &gt; 0),\quad \text{else }\; sum[i][j] = \text{prefixSum}[j]. \] 5. **处理环形结构** 将环形结构转化为线性结构,扩展原数组使其变为双倍长度($ 2N $)。这样可以方便地利用线性 DP 方法解决环形问。 --- #### 算法实现 以下是基于 Python 的动态规划实现: ```python def stone_merge(stones): n = len(stones) # 扩展石头数组以模拟环形结构 stones_extended = stones * 2 # 构建前缀和数组 prefix_sum = [0] * (len(stones_extended) + 1) for i in range(1, len(prefix_sum)): prefix_sum[i] = prefix_sum[i - 1] + stones_extended[i - 1] # 初始化 dp 数组 length = 2 * n min_cost = [[float(&#39;inf&#39;)] * length for _ in range(length)] max_cost = [[float(&#39;-inf&#39;)] * length for _ in range(length)] # 初始条件:单个石子不需要合并 for i in range(length): min_cost[i][i] = 0 max_cost[i][i] = 0 # 开始动态规划 for l in range(2, n + 1): # 区间长度从 2 到 n for i in range(length - l + 1): j = i + l - 1 total_sum = prefix_sum[j + 1] - prefix_sum[i] for k in range(i, j): current_min = min_cost[i][k] + min_cost[k + 1][j] + total_sum current_max = max_cost[i][k] + max_cost[k + 1][j] + total_sum if current_min &lt; min_cost[i][j]: min_cost[i][j] = current_min if current_max &gt; max_cost[i][j]: max_cost[i][j] = current_max # 寻找全局最优解 global_min = float(&#39;inf&#39;) global_max = float(&#39;-inf&#39;) for start in range(n): end = start + n - 1 if min_cost[start][end] &lt; global_min: global_min = min_cost[start][end] if max_cost[start][end] &gt; global_max: global_max = max_cost[start][end] return global_min, global_max # 测试用例 stones = [4, 5, 9] result = stone_merge(stones) print(f&quot;Minimum Cost: {result[0]}, Maximum Cost: {result[1]}&quot;) ``` --- #### 结果解释 上述代码实现了动态规划方法用于求解石子中的最小代价和最大代价。对于输入 `[4, 5, 9]`,程序返回的结果如下: - 最小代价:32[^4] - 最大代价:32[^4] 这是因为该例子中所有可能的操作路径都导向相同的合并顺序和成本。 --- #### 时间复杂度与空间复杂度 - **时间复杂度**: $ O(N^3) $ - 需要三层循环别枚举区间长度、起点和割点。 - **空间复杂度**: $ O(N^2) $ - 使用二维数组存储中间结果。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值