AcWing 167.木棒

考察的是对于剪枝DFS的运用。

思路:如果以DFS直接暴力来做,我们可以看到,其实这是一个组合型递归的问题。

我们DFS的是对于木棒的个数,以及对于木棒的长度进行遍历,从而找到合适的答案。

以这个角度来说,我们其实是在小木棍里面进行组合大木棒而已,也就是说,拼接的顺序是无关紧要的。那么我们就可以进行排序操作,而且是从大到小。为什么呢?为什么不能是从小到大呢?由于我们在拼接木棒的过程中,可选择的木棍很多,如果说我们一开始就选择比较大的,那么这个时候DFS的遍历数据是不是就缩小了一些呢?也就排除了比较大的一类数据。

然后就是剪枝了。我们从以下几个角度考虑:

1.首先我们应该清楚,如果一个木棒开始进行拼接第一个小木棍的时候,这时满足不了拼接条件,那么所有的小木棒在拼接这个木棒的时候都会失败,也就是在遍历这种可能性的时候没有方案。可以用反证法来证明这种观点,大家可以看这位大佬的证明,很详细。AcWing 167. 木棒(详细证明,图解版) - AcWing

2.同理,在我们拼接最后一个小木棒的时候刚好够长度,这个时候也是不能采取方案的,由于这样做出方案之后,后面拼接的小木棒也可能出现这种情况,但这个时候小木棒不会够用,就算移动到其他的木棒身上去,对于后面的木棒来说都是没有方案的。这个也可以用反证法证明。

3.以上判断完毕之后,这些都是可行性的剪枝分析,最上面的那个也是常见的遍历顺序分析。如果我们在上面的判断中知道了哪些木棒不能取,但是遍历后面的时候也可能出现和当前木棒一致的木棒长度,那么我们就可以进行跳过操作,不去遍历他,节省时间。

详细细节已经放在了注释里面:

上代码;

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<cmath> 
#include<vector>
#include<algorithm>
#include<stack>
#include<queue>
#include<sstream>
#include<numeric>
#include<map>
#include<limits.h>
#include<set>
#define MAX 70
#define _for(i,a,b) for(int i=a;i<(b);i++)
#define ALL(x) x.begin(),x.end()
using namespace std;
using PII=pair<int, int>;
int n,V,m,res=INT_MAX;
int length, sum;
int arr[MAX];
bool st[MAX];
bool dfs(int u, int nowLen, int start) {
    if (u * length == sum)//方案已经出来了,也就是说最终已经满足条件了
        return true;
    if (nowLen == length)return dfs(u + 1, 0, 0);//这个时候已经拼接完一个木棒了,所以不用多余耗费时间在已经完成的子任务上花去时间
    for (int i = start; i < n; i++) {
        if (st[i])//这个数已经用过了
            continue;
        if (nowLen + arr[i] <= length) {//还没有拼接完成
            st[i] = 1;
            if (dfs(u, arr[i] + nowLen, i + 1)) return true;//如果说按照这个方法拼下去有方案,说明这个方案是可行的
            st[i] = 0;//进行回溯,判断下一个木棍是否满足
        }
        if (!nowLen || nowLen + arr[i] == length)return false;
        int j = i + 1;
        while (j < n && arr[i] == arr[j])j++;//遇见与现在相同的数的时候,就可以跳过了,因为在上面已经判断完了,这种数不可能继续遍历下去让其他木棒成立的
        i = j - 1;
    }
    return false;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL); cout.tie(NULL);
    while (cin >> n, n) {
        sum =0;
        length=0;
        memset(st,0,sizeof st);
        _for(i, 0, n)
        {
            cin >> arr[i];
            sum += arr[i];
            length = max(length, arr[i]);
        }
        
        sort(arr, arr + n, greater<int>());
        while (1) {
            if (sum % length == 0 && dfs(0, 0, 0)) {
                cout << length << endl;
                break;
            }
            length++;
        }
    }
    
    return 0;
} 

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"粉粹木棒"(Crush the Stick)是一个经典的编程练习,通常用于教学或演示递归算法C++中的应用。这个练习中,你需要编写一个函数,计算一根木棒在一系列压力作用下最终断裂成零段的最小次数。木棒每受到一定的压力就会断成两段,直到无法再断为止。 以下是一个简单的C++代码示例,使用递归实现这个功能: ```cpp #include <iostream> using namespace std; int crushTheStick(int pressure, int currentLength) { // 基线条件:如果木棒长度为0,说明已经断成零段,返回0 if (currentLength == 0) { return 0; } // 递归条件:如果当前长度大于压力,表示木棒还能断,需要继续递归 else if (currentLength > pressure) { // 断一次,木棒长度减半,需要断裂次数加一 return 1 + crushTheStick(pressure, currentLength / 2); } // 如果木棒长度正好等于压力,意味着不需要进一步断裂 else { return crushTheStick(pressure, 0); // 木棒断裂后直接返回0 } } int main() { int pressure; // 输入压力值 cout << "Enter the pressure: "; cin >> pressure; int initialLength = 1000; // 或者其他初始木棒长度 int minBreaks = crushTheStick(pressure, initialLength); cout << "Minimum number of breaks needed to crush the stick: " << minBreaks << endl; return 0; } ``` 在这个代码中,`crushTheStick`函数接受压力值和当前木棒长度作为参数,然后根据规则进行递归。用户可以在`main`函数中输入压力值,并调用该函数获取结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值