搜索经典poj1011

这道题可以说是搜索剪枝例题中的经典,其中的剪枝条件太难想了
对于剪枝不了解的可以跳转点击这里可以看到

题面

题目传送门

乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过50个长度单位。
然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。
请你设计一个程序,帮助乔治计算木棒的可能最小长度。
每一节木棍的长度都用大于零的整数表示。
注意: 数据中可能包含长度大于50的木棒,请在处理时忽略这些木棒。
输入格式
输入包含多组数据,每组数据包括两行。
第一行是一个不超过64的整数,表示砍断之后共有多少节木棍。
第二行是截断以后,所得到的各节木棍的长度。
在最后一组数据之后,是一个零。
输出格式
为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。
输入样例:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例:
6
5

搜索+剪枝
在这个我们直接枚举每根木根的长度 肯定是从最大的那根木根开始枚举,直到所有木根的长度之和
然后进行dfs判断(具体看代码)
剪枝
1.优化搜索序列:优先选择较长的木根
2.排除等效冗余:对于当前的木根,记录最近一次尝试拼接失败的木根fail,因为fail已经失败,那就不需要尝试再次凭借和fail一样的木根了。
4.记忆化

c++代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
int test;
int vis[N];//标记木棍是否使用
int a[N], n;
bool flag;
void dfs(int deep, int num, int cnt) { 
//deep 记录木棍使用个数, num记录当前木棍拼接长度,cnt 记录当前到达的位置
     int i;
     if (num == 0) {
         i = 1;
         while (vis[i]) i ++;
         vis[i] = 1;
         dfs(deep + 1, num + a[i], i + 1);
         vis[i] = 0;
         return;
     }
     if(num == test) {
         if (deep == n) flag = true;
         else dfs(deep, 0, 0);
         return;
     }
     int fail = 0;//记录上一次失败的值,如果这次还是的话,那么肯定是不能选择的
     for (int i = cnt; i <= n; i ++ ) {
          //没有被访问,不是上一次失败的值,长度满足在test以内
          if (!vis[i] && num + a[i] <= test && a[i] != fail) {
              vis[i] = 1;
              dfs(deep + 1, num + a[i], cnt + 1);//开始下一次dfs
              if(flag) return;
              vis[i] = 0;// 失败了,就需要记录
              fail = a[i];
              if (num + a[i] == test) return;//如果num与a[i]相加正好是test,但是失败了,那么一定是失败了.
         }
 }
 if (!flag) return;//如果都失败了,那肯定是失败了
}
bool cmp(int a, int b) {
     return a > b;
}
int main () {
    while (scanf ("%d", &n)&& n) {
        int sum = 0, maxn = 0;
        for (int i = 1; i <= n; i ++ ) {
        scanf ("%d", &a[i]);
        sum += a[i];
//      maxn = max(a[i], maxn);
        }
      sort(a + 1, a + 1 + n, cmp);//排序优化搜索顺序
      for (int i = a[1]; i <= sum; i ++ ) {
          if (i * 2 > sum) {
              printf ("%d\n", sum);
              break;
            //省去一些不必要的枚举
          }
          if (sum%i) continue;//如果除不尽,肯定不满足题意
          memset(vis, 0, sizeof vis);//初始化
          test = i;
          flag = false;
          dfs(0, 0, 1);
          if (flag) {  //搜索成功,最小值就是它
              printf ("%d\n", i);
              break;
          }
     }
 }
 return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值