「这是我参与2022首次更文挑战的第8天,活动详情查看:2022首次更文挑战」。
这也是一个深度优先搜索的题目,但是包含了剪枝,提高了搜索效率。与普通的深度优先搜索题目有所不同。
题目
原文是英文,这里大致说一下题目是什么样的,原文就不贴出来了。
给定一定数目的相同长度的木棍,将他们随意剪断,但剪完后的数目不超过50根,现在要求把剪完后的木棍拼回去,拼成几根相同长度的木棍,并要求这些木棍尽可能要短。输入每次都有两行,一行是剪完后的数目,下一行是剪完后每根木棍的上长度。输出的就是这些木棍原来的长度。
思路
首先先确定木棍原先的长度再开始拼接,从这些木棍中最大的长度作为原先长度开始,往后逐渐加1,直到全部木棍的总长度。确定长度后,就可以得出原先木棍有多少根了。才开始搜索,从第一根木根开始搜索,找到的木棍和是原先的长度再从后面的木棍开始搜索。如果所有木棍都搜索完了, 成功的木棍根数跟开始搜索前确定的木棍根数一致,就可以退出了,答案已经出来了。大致的方向已经出来了,现在就只需要制定如何搜索的方法这个题目就算是完成了。那么我们需要如何搜索呢?从第一根木棍开始,从这根木棍开始往后搜索,如果这根木棍跟后面的木棍拼起来大于原定长度,则不拼,如果小于原定长度,则加上后继续往后拼,如果加到最后还是没有达到需要长度则直接返回。如果等于了原定长度,则用一个计数器,加1。当计数器的大小等于原定木棍根数则得出答案。每次达到需要长度后,找下一块木棍的第一根木棍。算法制定到这里就差不多了。还有一个非常重要的一点就是剪枝。如果这根木棍已经被使用,则可以跳过,如果这根木棍的上一根木棍未被使用,并且这根木根还跟上一根木棍长度一致,则也可以跳过,因为木棍长度相同,上一根都为被用过,这一根肯定也是用不着了。剪枝提高搜索效率,减少大量搜索时间。
代码
#include<bits/stdc++.h>
using namespace std;
int sum,len,n; //sum表示木棍总共的长度,len表示木棍的原定长度,n是木棍个数
int ans=0;//是否成功
int vis[50];//记录木棍是否被用过
bool cmd(int a,int b){
return a>b;
}
void dfs(int a[],int ss,int l,int index)//ss是成功的木块数,l表示当前木棍的长度,index是当前木棍下标
{
if(ans)
return ;
if(ss==sum/len)
{
ans=1;
return;
}
for(int i=index;i<n;i++)
{
if(vis[i]||!vis[i-1]&&a[i]==a[i-1]) //不符条件,直接跳出
{
continue;
}
if(l+a[i]==len)
{
ss++;
vis[i]=1;
if(ss==sum/len)
{
ans=1;
return;
}
for(int j=0;j<n;j++)
if(vis[j]==0)
{
dfs(a,ss,0,j);
vis[i]=0;
if(ans) return;
return;
}
}
if(l+a[i]<len)//如果最后一个木板拼上还是不够长度,说明这个长度不存在,退出。
{
vis[i]=1;
dfs(a,ss,l+a[i],i+1);
if(ans) return;//如果已经找到了,后续的搜索已经不需要了
vis[i]=0;
if(l==0) return;
}
}
}
int main(){
while(cin>>n,n)//题目要求一直输入,直到输入的是0退出
{
ans=0;
sum=0;
int a[50];
for(int i=0;i<n;i++)
{
cin>>a[i];
sum+=a[i];
}
sort(a,a+n,cmd);//排序,从大到小
for(int i=a[0];i<=sum;i++)//从这些木棍的最大值开始作为木棍的原定值
{
if(sum%i==0)
{
len=i;
memset(vis,0,sizeof(vis));
dfs(a,0,0,0);
if(ans)
{
cout<<len<<endl;
break;
}
}
}
}
return 0;
}