P1120 小木棍

文章讲述了如何使用深度优先搜索(DFS)结合剪枝策略解决一道编程题。题目要求从给定的被截断的小木棍中找到能拼接成整数个相同长度大木棍的最小长度。关键在于对DFS的优化,包括最优性剪枝(大木棍长度需被所有小木棍长度总和整除)和可行性剪枝(如降序排列木棍,避免重复尝试)。文章提供了代码示例并详细解释了剪枝方法。
摘要由CSDN通过智能技术生成

题目链接

标准数据洛谷P1120
弱数据47.112.32.87

题目

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入格式

第一行是一个整数 n n n,表示小木棍的个数。
第二行有 n n n 个整数,表示各个木棍的长度 a i a_i ai

输出格式

输出一行一个整数表示答案。

输入输出样例

输入
9
5 2 1 5 2 1 5 2 1
输出
6

说明/提示

对于全部测试点, 1 ≤ n ≤ 65 1 \leq n \leq 65 1n65 1 ≤ a i ≤ 50 1≤a_i≤50 1ai50

题意

有n个被截断的小木棍,需要将它们连接成若干个长度相等的大木棍(小木棍必须拼完,不能剩余)
求大木棍的最小长度

思路

先枚举大木棍的长度
然后用dfs枚举是否可以用截断的木棍来拼出整数个大木棍,如果可以,求出最短的大木棍即可
但纯dfs对于这道题肯定会超时的,所以我们要对dfs进行剪枝,将时间复杂度降下来。对于此题,可以从最优性和可行性剪枝上分析
最优性剪枝

  1. 因为要拼出整数个大木棍并且不能剩余,所以大木棍的长度要被所有小木棍的长度总和所整除
  2. 所有大木棍的长度肯定要大于等于截断的小木棍中长度最大的那根

可行性剪枝

  1. 对小木棍的长度进行降序排序,一根长的木棍肯定比几根短的木棍要更快拼完大木棍,但不容易组合,所以由大到小排序,先拼大部分,然后再用短的木棍灵活匹配,这样会更快速地拼完
  2. 因为 1 中已对截断的小木棍进行降序排序,所以当我们枚举了第i个小木棍后,就不用枚举编号为 1 ∼ i − 1 1\sim i-1 1i1的木棍了,而下一次枚举从 i + 1 i+1 i+1即可
  3. 排除等效冗余:对于当前木棍,记录最近一次尝试拼接失败的木棍,因为它失败了,那么肯定之后不能尝试再次凭借和他长度一模一样的木棍。因为他们是一模一样,没有任何差别,那么A死了,后面的A自然也得死,虽然他们下标不一样。
  4. 若加入第i根小木棒后超过了大木棒的长度,那就需要转而枚举更小长度的小木棍
  5. 如果放第一根木棒和放最后一根木棒都失败了,那么该长度一定不合法。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,target,l[66],max_start,cnt,s;
bool vis[66];
bool dfs(int x,int sum,int last){
	if(x>cnt){//判断边界 
		printf("%lld",target);
		exit(0);//结束程序 
	}
	if(sum==target)return dfs(x+1,0,0);//拼完了一根大木棒,继续拼下一根,累加长度清零,从头开始枚举 
	int same=0;//可行性剪枝3
	for(int i=last+1;i<=n;i++){//可行性剪枝2 
		if(!vis[i]&&!(l[i]==same)&&sum+l[i]<=target){//!(l[i]==same)为可行性剪枝3   sum+l[i]<=target为可行性剪枝4 
			vis[i]=true;
			if(dfs(x,sum+l[i],i))return true;//拼接第i根小木棒 
			vis[i]=false;
			same=l[i];//可行性剪枝3
			if(sum==0||sum+l[i]==target)return false;//可行性剪枝5 
		}
	}
	return false;
}
bool cmp(int x,int y){
	return x>y;
}
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&l[i]),s+=l[i],max_start=max(max_start,l[i]);
	sort(l+1,l+1+n,cmp);//可行性剪枝1 
	for(int i=max_start;i<=s;i++){//最优性剪枝2 
		if(s%i)continue;//最优性剪枝1
		cnt=s/i,target=i;// 
		dfs(1,0,0);//当拼接成功,退出循环并输出 
	}
	return 0;
}

总结

进行深度优先搜索时,需注意剪枝,常用剪枝的方式有:排除等效冗余、可行性剪枝、最优性剪枝、记忆化等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值