1 条题解
-
0
算法
(DFS 剪枝优化)
搜索顺序:根据木棒的长度从小到大枚举每根木棒,对于每根木棒,枚举可以由哪些木棍拼成,如果所有的木棍拼成了长度相等的多个木棒,说明找到了答案,否则木棒长度加 继续搜索。
为什么是正确的?
因为题目要求保证拼凑成功的前提下,还有分组尽可能少,即木棒数量尽可能少,所以我们从小到大枚举每根木棒的长度,第一次找到答案时就是最优解。剪枝优化:(各种优化,非常多)
-
剪枝 1:
sum % length == 0只有 是 的约数才有可能凑出多个等长的木棒 -
剪枝 2:优化搜索顺序,木棍长度从大到小排序,可以减少搜索的分支
-
排除等效冗余优化
剪枝 3-1:确定每根木棒中木棍的枚举顺序,因为我们的方案和顺序没有关系,以组合的形式枚举方案可以少搜很多重复方案 -
可行性剪枝
剪枝 4-1:如果当前木棍没有搜到方案,则跳过所有长度相等的木棍
剪枝 4-2:如果是木棒的第一根木棍就搜索失败了,则一定搜不到方案
剪枝 4-3:如果是木棒的最后一根木棍(+ 上它木棒长度正好是 length)搜索失败了,也一定搜不到方案
证明 4-2:如果在当前木棒 的开头换一根木棍 可以搜到方案,那么原来这根木棍 一定在后面的某根木棒 中,我们交换木棒 的木棍 和开头的木棍不会影响答案,那么此时就出现了以木棍 开头的木棒搜到了答案,这我们的已知矛盾,所以只要有一根木棒的开头搜不到方案,不管这个位置放哪根木棍都搜不到方案。
证明 4-3:如果我们换几根 / 一根木棍 放在当前木棒 的最后可以搜到答案,那么原来这跟木棍 也一定在后面的某根木棒 中,因为 和 的长度相等,此时把 交换过去,此时就出现了以木棍 结尾的木棒搜到了答案,这我们的已知矛盾,所以只要有一根木棒的最后一根木棍搜不到方案,不管这个位置放哪根木棍都搜不到方案。
注意:这里有个前提是,当前木棒 + 最后一根木棍的总长度正好是 length,上面证明才成立。
具体实现看代码及注释。
时间复杂度
指数级别
空间复杂度
C++ 代码
#include<bits/stdc++.h> using namespace std; const int N = 70; int n; int length, sum; // length 表示枚举的木棒的长度,sum 表示当前已经拼好的木棒的总长度 int w[N]; // 每根木棍的长度 bool st[N]; // 状态数组 // u:当前正在拼哪根木棒,s:当前木棒中已有木棍的总长度,start:从哪个位置开始枚举木棍 bool dfs(int u, int s, int start) { if (u * length == sum) return true; // 如果当前木棒的数量 * 长度 = sum,则找到了答案,直接返回 true if (s == length) return dfs(u + 1, 0, 0); // 如果当前木棒的长度等于了 length,则新开一根木棒 // 否则枚举从 start 开始枚举将哪根木棍拼到当前木棒中 for (int i = start; i < n; i ++ ) { // 剪枝 3-1:确定枚举顺序,以组合的形式枚举方案 if (st[i]) continue; // 如果当前木棍用过了,则跳过 if (s + w[i] <= length) { // 如果当前木棒拼上该木棍没超过 length st[i] = true; if (dfs(u, s + w[i], i + 1)) return true; // 继续搜索当前木棒的下一根木棍拼哪根 st[i] = false; // 执行到这 dfs(u, s + w[i], i + 1) 为 false: 说明当前木棍搜索失败了 // 剪枝 4-2:如果是木棒的第一根木棍就搜索失败了,则一定搜不到方案 if (!s) return false; // s = 0 表示刚才木棒拼的是第一根木棍 // 剪枝 4-3:如果是木棒的最后一根木棍搜索失败了,也一定搜不到方案 if (s + w[i] == length) return false; // 剪枝 4-1:如果当前木棍没有搜到方案,则跳过所有长度相等的木棍 int j = i; while (j < n && w[i] == w[j]) j ++ ; i = j - 1; } } return false; } int main() { while (cin >> n, n) { // 多组测试数据清空 memset(st, 0, sizeof st); sum = length = 0; for (int i = 0; i < n; i ++ ) { cin >> w[i]; if(w[i] > 50) continue; sum += w[i]; length = max(length, w[i]); } // 剪枝 2:优化搜索顺序,木棍长度从大到小排序 sort(w, w + n); reverse(w, w + n); for(int i = 0; i < n; i++) if(w[i] > 50) st[i] = true; while (true) { // 循环一定会退出,最坏情况下所有木棍拼一根木棒 if (sum % length == 0 && dfs(0, 0, 0)) { // 剪枝 1:只有 length 是 sum 的约数才有可能凑出多个等长的木棒 cout << length << endl; break; } length ++ ; // 当前 length 无法得到答案,则长度 + 1 } } return 0; } -
- 1
信息
- ID
- 101
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 8
- 标签
- 递交数
- 62
- 已通过
- 8
- 上传者