洛谷-1120 小木棍 [数据加强版]

本文探讨了一道有趣的编程挑战,乔治将木棍随意砍断,需通过算法找到原始木棍的最小可能长度。文章提供了详细的算法思路和剪枝技巧,包括从最大木板开始搜索、避免重复搜索、判断是否能凑成整块等关键步骤。

题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入输出格式
输入格式:
共二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65N≤65
(管理员注:要把超过50的长度自觉过滤掉,坑了很多人了!)
第二行为N个用空个隔开的正整数,表示N根小木棍的长度。
输出格式:
一个数,表示要求的原始木棍的最小可能长度

输入输出样例
输入样例#1:
9
5 2 1 5 2 1 5 2 1

输出样例#1:
6

解释:暴力很好写,主要是剪枝,这里有4点,1.从最大木板开始搜,2.按从大向小搜避免重复,3.如果凑成整块后,以后的小的凑不出来,那么必然凑不出来,4.如果刚开始后大的凑不出来,小的必然也凑不出来。

#include<cstdio>
#include<cstdlib>
const int N = 70 ;
int n , cnt , tot , maxn , minn , tm[ N ]; 
void dfs( int res , int sum , int target , int p ) {
    if(res==0){
        printf("%d", target  );
        exit(0);
    }
    if(sum==target){
        dfs(res - 1,0,target, maxn );
        return;
    }
    for( int i = p ; i >= minn ; i -- ){ 
        if( tm[ i ] && i + sum <= target ){
            tm[ i ] -- ;
            dfs( res , sum + i , target , i );
            tm[ i ] ++ ;
            if ( sum == 0 || sum + i == target )
                break;
        }
    }
    return;
}
int main() {
    scanf("%d" , &n ) ;
    minn = N ;
    int temp;
    while( n -- ) {
        scanf("%d" , &temp );
        if( temp <= 50 ) {
            cnt ++;
            tm[ temp ] ++;
            tot += temp;
            maxn = maxn > temp ? maxn : temp ; 
            minn = minn < temp ? minn : temp ;
        }
    }
    for( int i = maxn ; i <= tot ; i ++ ) {
        if( tot % i == 0 ) {
            dfs( tot / i , 0 , i , maxn );
        }
    }
    printf("%d" , tot );
    return 0;
}
### 洛谷 P1120木棍问题的解决方案与编程指导 洛谷 P1120 是一道经典的深度优先搜索(DFS)与剪枝优化结合的问题。题目要求将一组火柴(木棍)拼接成若干根长度相同的木棍,并找出最小的可能拼接长度。由于火柴数量较多,直接穷举所有组合效率极低,因此必须采用剪枝策略进行优化。 #### 深度优先搜索与剪枝优化 在本题中,深度优先搜索用于尝试所有可能的拼接方式。每一步尝试将火柴放入当前拼接的木棍中,若无法满足目标长度则回溯。为了提高效率,采用了多种剪枝策略,例如跳过重复长度的火柴、避免无效搜索等。这些剪枝策略大大减少了无效的搜索路径,使算法在合理时间内完成[^3]。 ```cpp bool dfs(int num, int now, int start) { if (num == cnt) return true; if (now == len) return dfs(num + 1, 0, 0); int prev = -1; for (int i = start; i < n; i++) { if (used[i] || now + stick[i] > len) continue; if (stick[i] == prev) continue; used[i] = 1; prev = stick[i]; if (dfs(num, now + stick[i], i + 1)) return true; used[i] = 0; if (now == 0) return false; } return false; } ``` #### 火柴排序的必要性 火柴长度从大到小排序是优化的关键。优先选择较长的火柴可以更快地填充当前拼接的木棍,减少后续回溯的可能性。此外,从大到小排序也有助于剪枝策略的实现,例如跳过重复长度的火柴。 ```cpp sort(stick, stick + n, cmp); ``` #### 剪枝策略的具体实现 本题的剪枝策略主要包括: - **跳过无效火柴**:若当前火柴长度超过目标长度,则跳过。 - **避免重复搜索**:连续多根长度相同的火柴,若前一根未被使用,则跳过当前火柴。 - **剪枝当前木棍拼接失败的情况**:若当前火柴是某根木棍的第一根失败,或者作为最后一根未成功拼接,则直接剪枝。 这些策略在代码中通过条件判断和回溯实现,确保搜索过程高效[^4]。 #### 目标长度的确定 目标长度的计算基于所有火柴的总长度 `sum` 和拼接后的木棍根数 `cnt`。枚举所有可能的目标长度 `len`,从最小的可能长度开始尝试,一旦找到满足条件的解即可返回。目标长度必须满足 `sum % len == 0`,且 `cnt = sum / len`。 ```cpp for (int i = 0; i < n; i++) { if (stick[i] > 50) continue; len = stick[i]; if (sum % len != 0) continue; cnt = sum / len; memset(used, 0, sizeof(used)); if (dfs(0, 0, 0)) { cout << len << endl; break; } } ``` #### 避免重复搜索相同长度火柴 为了避免重复搜索相同长度的火柴,在 DFS 过程中设置了一个变量 `prev`,用于记录上一根火柴的长度。若当前火柴长度与 `prev` 相同且未被使用,则跳过当前火柴。这一策略有效减少了重复的搜索路径[^3]。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值