小木棍
枚举原木棍长度,DFS 拼接配多重剪枝
正文
// 原题:https://oj.yecheng.tv/p/T1442
// 题意:一堆等长木棍被砍成 n 段(每段不超过 50),求原始木棍的最小可能长度。
// 思路:枚举原始长度 len(必须是总长的约数且不小于最长段),用 DFS 拼木棍。
// 1. 木棍长度从大到小排序,长的先放,分支更少。
// 2. 拼当前木棍时只往后找,避免枚举同一组合的不同排列。
// 3. 关键剪枝:某段作为一根新木棍的第一段放不进去就整体失败;刚好拼满一根却失败也直接回溯。
// 复杂度:指数级搜索,靠剪枝在 n <= 60 时很快出解
// 易错点:len 从最长段开始枚举并且必须是总长的约数,漏掉这个条件会做大量无用枚举。
// 易错点:同一层搜索中相同长度的木棍只尝试一次,否则会重复搜索完全等价的分支导致超时。
#include <bits/stdc++.h>
using namespace std;
int n, len, need;
vector<int> a;
vector<int> used;
bool dfs(int done, int cur, int start){
if(done == need) return true;
if(cur == len) return dfs(done + 1, 0, 0);
int prev = -1;
for(int i = start; i < n; i++){
if(used[i] || a[i] == prev) continue;
if(cur + a[i] > len) continue;
used[i] = 1;
if(dfs(done, cur + a[i], i + 1)) return true;
used[i] = 0;
prev = a[i];
if(cur == 0) return false;
if(cur + a[i] == len) return false;
}
return false;
}
int main(){
if(!(cin >> n)) return 0;
a.resize(n);
int sum = 0, mx = 0;
for(int i = 0; i < n; i++){
cin >> a[i];
sum += a[i];
mx = max(mx, a[i]);
}
sort(a.begin(), a.end(), greater<int>());
used.assign(n, 0);
for(len = mx; len <= sum; len++){
if(sum % len) continue;
need = sum / len;
if(dfs(0, 0, 0)) break;
}
cout << len << "\n";
return 0;
}
一本通·提高篇的其它内容
- 活动安排
- 种树
- 喷水装置
- 加工生产调度
- 智力大冲浪
- 数列极差
- 数列分段
- 线段
- 家庭作业
- 钓鱼
- 糖果传递
- 愤怒的牛
- Best Cow Fences
- 曲线
- 数列分段 II
- 扩散
- 灯泡
- 传送带
- 数的划分
- 生日蛋糕
- Addition Chains
- 埃及分数
- 平板涂色
- 质数方阵
- 靶形数独
- 电路维修
- 魔板
- Knight Moves
- 棋盘游戏
- Keyboarding
- 移动玩具
- 山峰和山谷
- Oulipo
- 图书管理
- Power Strings
- Seekthe Name, Seek the Fame
- Friends
- A Horrible Poem
- Beads
- Antisymmetry
- 门票
- 收集雪花
- 剪花布条
- Power Strings
- Radio Transmission
- OKR-Periods of Words
- 似乎在梦中见过的样子
- Censoring
- Phone List
- The XOR Largest Pair
- Nikitosh 和异或
- Immediate Decodability
- L 语言
- Secret Message 秘密信息
- 背单词
- The Xor-longest Path
- Keywords Search
- 玄武密码
- Censoring
- 单词