You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

83 lines
4.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// #include <bits/stdc++.h>
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 70;
int a[N]; // 用来装每个木棍的长度
int n; // 木棍个数
int len; // 组长度
int sum; // 总长度
bool st[N]; // 标识某个木棍是不是使用过了
/*
u :木棒号,下标从0开始
last:最后一个木棒目前的长度
start:从哪个索引号开始继续查找
只要搜索到解就停止的dfs问题一般用bool类型作为返回值因为这样搜到最优解就返回true用dfs的返回值作为条件
就可以瞬间退出递归了。比上一题中专门设置标志变量的方法要更好。
*/
bool dfs(int u, int last, int start) {
// 因为填充的逻辑是填充满了一个才能走到下一个面前所以如果成功到达了第u个前面的话说明前u-1个都是填充满的
// 如果在第u个面前,检查到木棒长度 乘以 木棒数量 等于总长度,说明完成了所有填充工作,递归终止
if (len * u == sum) return true;
// 因为每一个木棒原来都是客观存在的,所以,每组木棍必须可以填充满一个木棒
// 不能填充满,就不能继续填充下一个木棒
if (last == len) return dfs(u + 1, 0, 0); // 注意当一组完成时下一组从0开始搜
// 在当前木棒没有填充满的情况下,需要继续找某个木棍进行填充
// start表示搜索开始的位置
// 防止出现 abc ,cba这样的情况我们要的是组合不要排列,每次查找选择元素后面的就可以了
for (int i = start; i < n; i++) {
// 使用过 or 超过枚举的木棒长度
if (st[i] || last + a[i] > len) continue;
// 准备将i号木棍放入u这个木棒中
st[i] = true; // 标识i号木棍已使用过
if (dfs(u, last + a[i], start + 1)) return true; // 将i号木棍放入u号木棒中
st[i] = false; // 恢复现场
// 可行性剪枝
// 优化4如果在第u组放置失败且此时第u组长度为0这是最理想的状态这种情况都放不下那么占用了一些空间的情况下就肯定更放不下
if (last == 0) return false;
// 优化5如果加入一个元素后某个分组和等于len了但是后续搜索失败了(后续肯定是开新组并且last从0开始),则没有可行解,和4是等价的。
if (last + a[i] == len) return false;
// 优化6冗余性剪枝
// 如果当前未放置成功,则后面和该木棒长度相等的也一样,直接略过即可
while (i < n - 1 && a[i] == a[i + 1]) i++;
}
return false;
}
int main() {
// 多组测试数据以0结束输入
while (scanf("%d", &n) && n) {
// 多组数组初始化
sum = 0; // 木棒总长清零
memset(st, false, sizeof st); // 清空使用状态桶数组
// 录入n个木棍的长度
for (int i = 0; i < n; i++) scanf("%d", &a[i]), sum += a[i]; // 总长度
// 优化1按小猫思路由大到小排序因为大卡车越往前越好找空放越往后越不容易找空
// 根据搜索顺序优化,枚举长度小的分支比长度大的分支要多,所以先枚举长度大的
sort(a, a + n, greater<int>());
// 枚举每一个可能的木棒长度,注意这是一个全局量,因为需要控制递归的出口
for (len = a[0]; len <= sum; len++) { // 优化2从最短的木棍长度开始可以减枝
// 优化3如果总长度不能整除木棒长度那么不能按这个长度设置木棒长度
if (sum % len) continue;
// 在当前len长度的基础上开始搜索
if (dfs(1, 0, 0)) {
printf("%d\n", len); // 找到最短的木棒长度
break; // 找到一个就退出
}
}
}
return 0;
}