题解 Sticks 小木棍

题目链接:

UVA
luogu RemoteJudge需登UVA
luogu 加强版

题目描述:

每组数据给出N根小木棍,把它们拼接成若干根长度相等的木棍,求该长度的最小值。

题解:

这题似乎就是暴搜加剪枝优化,不过剪枝比较复杂。
先上代码:

#include 
using namespace std;

int a[100],v[100],n,len,cnt;
               //正在拼 当前长度 上一根小木棍 
bool dfs(int stick,int cab,int last){  
	if(stick>cnt) return true;      //正在拼的大于总数 即全拼完
    //这一根拼完,拼下一根 
	if(cab==len) return dfs(stick+1,0,1); 
	int fail=0;         //剪枝(1)
	for(int i=last;i<=n;i++){    //剪枝(2)
		if(!v[i]&&cab+a[i]<=len&&fail!=a[i]){
			v[i]=1;     //剪枝(3)
			if(dfs(stick,cab+a[i],i+1)) return true;   //递推
			fail=a[i];
			v[i]=0; 
            //剪枝(4)
			if(cab==0||cab+a[i]==len) return false;  
		}
	}
	return false;
}

int main(){
	while(cin>>n){
		int sum=0,val=0;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			if(a[i]>50){   //加强版要把超过50的长度过滤掉
				i--;
				n--;
				continue;
			}
			sum+=a[i];
			val=max(val,a[i]);
		}
		sort(a+1,a+n+1);
		reverse(a+1,a+n+1);
		for(len=val;len<=sum;len++){
        //木棍长度必定是长度和的约数
			if(sum%len) continue;    
			cnt=sum/len;      
			memset(v,0,sizeof(v));
			if(dfs(1,0,1)) break;
		}
		cout<<len<<endl;
	}
	return 0;
}

4个剪枝:
(1) 对于当前原始木棒,记录最近一次尝试拼接的木棍长度。如果分支搜索失败回溯,不再尝试向该木棍中拼接其他相同长度的木棍(必定也会失败)
(2)使加入小木棍的长度递减
(3)用vis数组标记每根木棍是否用过。因为如果cab==len,last会重置为1
(4)如果当前长棍剩余的未拼长度等于当前木棍的长度或原始长度,继续拼下去时却失败了(前面没有return true),就直接回溯并改之前拼的木棍。

当前长棍剩余的未拼长度等于当前木棍的长度时,这根木棍在最优情况下显然是拼到这(如果用更多短木根拼完剩下的这段,把这根木棍留到后面显然不如把更多总长相等的短木棍扔到后面)。如果在最优情况下继续拼下去失败了,那肯定是之前的木棍用错了,回溯改即可。

写作时间:

2019-7-31

你可能感兴趣的:(2019暑,Hello,World)