题目描述
MELON 有一堆精美的雨花石(数量为n,重量各异),准备送给S和W。MELON希望送给俩人的雨花石重量一致,请你设计一个程序,帮MELON确认是否能将雨花石平均分配。
输入描述
第1行输入为雨花石个数:n,0<n<31。
第2行输入为空格分割的各雨花石重量:m[0]m[1].. m[n -1],0<m[k]< 1001.
不需要考虑异常输入的情况。
输出描述
如果可以均分,从当前雨花石中最少拿出几块,可以使两堆的重量相等;如果不能均分,则输出-1。
示例1
输入
4
1 1 2 2
输出
2
说明
输入第一行代表共4颗雨花石,
第二行代表4颗雨花石重量分别为1、1、2、2。均分时只能分别为1,2,需要拿出重量为1和2的两块雨花石,所以输出2。
示例2
输入
10
1 1 1 1 1 9 8 3 7 10
输出
3
说明
输入第一行代表共10颗雨花石,
第二行代表4颗雨花石重量分别为1、1、1、1、1、9、
8、3、7、10。
均分时可以1.1.1.1.1.9.7和10.8.3,也可以1.1.1.1.9.8和10,7.3,1,或者其他均分方式,但第一种只需要拿出重量为10,8,3的3块雨花石,第二种需要拿出4块,所以输出3(块数最少)。
解题思路
这个问题本质上是一个“子集和”问题,即给定一组不同重量的雨花石,判断是否可以将这些雨花石分成两个子集,使得两个子集的重量相等。如果能分,则还需要计算最少拿出的雨花石数量以实现这个目标。
1. 判断是否可以平分
首先需要判断这些雨花石的总重量 total_weight 是否是偶数:
- 如果 total_weight 是奇数,那么肯定无法平分,直接输出 -1,因为两个子集的重量不可能相等。
- 如果 total_weight 是偶数,目标就是将雨花石分成两个重量为 total_weight / 2 的子集。
2. 转换为背包问题
将问题转化为经典的“背包问题”:
- 我们需要在给定的雨花石集合中,找到一个子集,其总重量恰好等于 total_weight / 2。如果能找到这个子集,说明可以平分雨花石。
- 同时,还要最小化子集的元素数量。
3. 动态规划的核心思想
使用动态规划解决这个问题。设 dp[i] 为达到重量 i 时所需的最少雨花石数量。
- 初始化:dp[0] = 0,即凑出重量 0 不需要任何雨花石。
- 其余的 dp[i] 初始化为一个很大的值(比如无穷大 ∞),表示还无法凑出重量 i。
- 遍历每个雨花石的重量,并更新动态规划数组 dp,确保每个雨花石只使用一次:
- 对每个雨花石 weights[i],从目标重量 target 开始,逐步往回更新到 weights[i]:
- 如果已经能够凑出重量 j - weights[i](即 dp[j - weights[i]] 不是无穷大),那么更新 dp[j] 为:
dp[j] = min(dp[j], dp[j - weights[i]] + 1)
- 这里 dp[j - weights[i]] + 1 表示加入当前雨花石后,能够凑出重量 j,并更新所需雨花石的最小数量。
4. 判断最终结果
- 在遍历完所有雨花石后,检查 dp[target]的值。
- 如果 dp[target] 仍然是初始值(例如 ∞),说明无法凑出重量为 target 的子集,即不能平分雨花石,此时输出 -1。
- 否则,dp[target] 的值就是达到目标重量所需的最少雨花石数量,直接输出这个值。
python解法:
def min_stones_to_split(n, weights):
total_weight = sum(weights)
# 如果总重量是奇数,无法平分
if total_weight % 2 != 0:
return -1
target = total_weight // 2
# dp[i] 表示凑出重量 i 所需的最少雨花石数量
dp = [float('inf')] * (target + 1)
dp[0] = 0 # 凑出重量 0 需要 0 块雨花石
# 遍历每个雨花石重量
for weight in weights:
for j in range(target, weight - 1, -1):
if dp[j - weight] != float('inf'):
dp[j] = min(dp[j], dp[j - weight] + 1)
# 如果 dp[target] 仍然是 float('inf'),说明无法平分
return dp[target] if dp[target] != float('inf') else -1
# 测试
if __name__ == "__main__":
n = int(input()) # 雨花石个数
weights = list(map(int, input().split())) # 输入雨花石重量
result = min_stones_to_split(n, weights)
print(result)