为何用combinations方法求解CCC17S3计数问题失败?
2017 CCC S3 解法问题排查
我针对2017年CCC竞赛S3题写了一份解法,代码如下:
from itertools import combinations num_pieces = int(input()) pieces = input() pieces_list = list(combinations([int(num) for num in pieces.split()], 2)) possible_heights = [0]*4000 for item in pieces_list: possible_heights[item[0]+item[1]-1] += 1 print(max(possible_heights), possible_heights.count(max(possible_heights)))
我的思路是遍历所有木板长度的两两组合,计算它们构成的高度并统计次数,列表索引对应木板高度,列表最大值为最多能搭建的同高度木板数量,其出现次数为符合条件的不同高度数。但我发现代码似乎未考虑到能延长木板长度的某些组合,希望帮忙排查问题。
代码核心错误分析
- 仅考虑双木板拼接支撑:完全忽略了单木板作为支撑的情况,这会遗漏大量最优解。比如有5块长度为2的木板,最优解是做5个高度为2的支撑,但你的代码只会统计两两拼接成高度4的支撑,最多输出2个,明显错误。
- 未考虑木板使用限制:
combinations统计的是所有可能的双木板组合数,但每块木板只能用一次。比如三块长度2的木板,combinations会统计3次高度4的组合,但实际只能搭建1个高度4的支撑,剩下一块2还能做1个支撑,总支撑数是2,远大于你的代码输出的1。 - 未覆盖多木板拼接场景:完全没考虑三块及以上木板拼接成同一高度支撑的情况,导致部分合法组合被遗漏。
正确解法思路与代码
思路
- 统计木板频率:先统计每个长度的木板出现次数,方便后续计算。
- 枚举最大可能支撑数:从总木板数(最大可能值)向下枚举,直到找到第一个存在至少一个高度
h,能搭建出该数量的同高度支撑。 - 验证高度合法性:对每个候选支撑数,检查是否存在高度
h,使得现有木板能组合出k个和为h的支撑(每块木板仅用一次)。
代码实现
def main(): import sys input = sys.stdin.read().split() n = int(input[0]) lengths = list(map(int, input[1:n+1])) # 统计每个长度的木板数量,长度范围1~200 freq = [0] * 201 total_len = 0 max_len = 0 for l in lengths: freq[l] += 1 total_len += l if l > max_len: max_len = l # 从最大可能的k开始枚举(总木板数) for k in range(n, 0, -1): # h的最小可能值是max_len(最长木板必须能作为支撑或被拼接) # h的最大可能值是total_len // k(总长度至少要够k个支撑) min_h = max_len max_h = total_len // k if min_h > max_h: continue count = 0 # 检查每个可能的h for h in range(min_h, max_h + 1): used = 0 valid = True # 复制频率数组避免修改原数据 temp_freq = freq.copy() # 先处理长度等于h的木板,直接作为支撑 used += temp_freq[h] temp_freq[h] = 0 # 处理长度小于h的木板,从小到大处理避免重复计算 for l in range(1, h): if temp_freq[l] == 0: continue # 先处理和h-l的组合 if h - l > l: pairs = min(temp_freq[l], temp_freq[h - l]) used += pairs temp_freq[l] -= pairs temp_freq[h - l] -= pairs # 处理多个l拼接成h的情况(l * m = h) if h % l == 0: m = h // l num_supports = temp_freq[l] // m used += num_supports temp_freq[l] -= num_supports * m # 检查是否能达到k个支撑 if used >= k: count += 1 if count > 0: print(k, count) return # 最坏情况,只能做1个支撑(总长度) print(1, 1) if __name__ == "__main__": main()
代码说明
- 统计木板频率后,从最大可能的支撑数
k开始枚举,一旦找到合法的k就直接输出,保证效率。 - 对每个
k,遍历可能的高度h,通过贪心算法验证是否能凑出k个和为h的支撑:优先用等长木板直接作为支撑,再用两两组合,最后用多块短木板拼接。 - 当找到第一个合法的
k时,统计对应的合法h数量并输出,即为最优解。
内容的提问来源于stack exchange,提问作者Joe Dahl
相关产品推荐
相关产品推荐

