题解0007:小木棍(P1120)

 

 (错误记录)

题目链接:https://www.luogu.com.cn/problem/P1120

题目描述:几根同样长的木棍,小冥把它们随意砍成了n段;

     然后他又吃饱了撑的想把木棍拼上;

     但是这个小冥是小年痴呆,他忘了他原来是咋切的了;

     现在要写一段程序,输入n和切断的每段木棍的长,输出原始木棍的最小可能长度。

题目思路:搜索剪枝,这道题搜索其实很好说,直接从小到大枚举长度,然后递归判断可行性;

     难点还是在剪枝上,如果不用剪枝的话只能得9分左右,这道题要用到7个剪枝;

     具体的详见代码;

代码:

#include<bits/stdc++.h>
using namespace std;
int n,arr[66]={0},ans,brr[66]={0},we,wer;//arr存木棍长度,brr存木棍是否被用过 
bool cmp(int a,int b){//将所有木棍从大到小排序 
	return a>b; 
}
bool dfs(int a,int b,int c){//a是假定小木棍的剩余长度,b是循环初始值,c是木棍根数 
	if(c==wer){//木棍根数对上了 
		return 1;
	}
	if(a==0){//一根木棍拼完 
		if(dfs(we,1,c+1)){//根数+1 
			return 1;
		}
	}
	for(int i=b;i<=n;++i){
	//剪枝3:下次循环的初始值完全可以接上上次循环的结束值 
		if(arr[i]<=a&&brr[i]!=1){//木棍没被用过&&长度没超 
			brr[i]=1;//标记 
			if(dfs(a-arr[i],i+1,c)){//-去长度 
				return 1;
			}
			brr[i]=0;//回溯 
			if(a==arr[i]||a==we){
			//剪枝4:如果这个木棍长度与剩余长度一样,这已经是最优解了,还不行,那直接返
			//剪枝5:如果剩余长度与假定木棍长度一样,和剪枝4一样,直接返 
				return 0;//如果写break可能过不了 
			}
			while(arr[i+1]==arr[i]){
			//剪枝6:因为排了序,所以有很多相等的数在一起,遇到相等的数直接跳 
				i++;
			}
		}
	}
	return 0;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>arr[i];
		ans+=arr[i]; 
	}
	sort(arr+1,arr+n+1,cmp);
	for(int i=arr[1];i<=ans/2;++i){//这个数从最小木棍长度开始枚举 
	//剪枝1:如果枚举到所有木棍长度+起来/2还没有找到,那结果就一定是所有数加起来了,往下搜没意义了 
		if(ans%i==0){
		//剪枝2:只有能被总长度整除的数才有可能是结果,不满足条件的直接扔掉 
			we=i;
			wer=ans/we;
			if(dfs(we,1,0)){//递归判断可行性 
				cout<<we;
				return 0;//可以直接结束 
			}
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2022-03-25 17:39  uf0_金币灰黄^w.h  阅读(74)  评论(0)    收藏  举报