POJ/UVA307 1011 Sticks (DFS +强力剪枝 (经典))

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=243

题意:有n根木棍,要求将所有木条拼接成等长的若干木条,求原始拼接长度的最小值

分析:dfs+剪枝题目说拼接总长度不大于50,数组长度要开到100以上吧(因为这个问题,t了我好久大哭

        强力剪枝6连:

        1.枚举原始木棒长度从max-sum/2,如果在此部分还没有找到答案,则sum为答案

        2.长度从大到小排好序,如果枚举到当前的len,sum%len||a[0]>len那么这个长度肯定是不行的,用while,直接i++

       3.连续相同的数组中元素,如果:i有前导,且a[i]=a[i-1],且a[i-1]还没有被用掉,说明这个数是不行的,如6,4,4,1,可以想象

        4.如果当时前正在组的木棒长度为0,且当前遍历到的最大的可用a[i]不能达成成就,说明这个a[i]在组成的过程中一定不会用到,与题目矛盾,也即这种情况不可能,剪枝,return 0

        5.如果当前的长度加上a[i]组成一个len但是还是不能达成总成就(有若干len长的原始木棒),说明前面都是的,剪枝,return


代码:

#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
const int maxn=1e5 + 10;
int a[maxn];
int vis[maxn];
int sum=0;
int n;

int cmp(int a,int b){return a>b;}

int dfs(int len,int cur,int other,int cnt){
	if(cnt==sum/len)return 1;
	for(int i=other;i<n;i++){
		while(i&&a[i]==a[i-1]&&!vis[i-1])i++;
		if(cur+a[i]<=len&&!vis[i]){
			if(cur+a[i]<len){
				vis[i]=1;
				if(dfs(len,cur+a[i],i+1,cnt)){
					return 1;
				}
				vis[i]=0;
				if(cur==0){
					return 0;
				}
			}
			if(cur+a[i]==len){
				vis[i]=1;
				if(dfs(len,0,0,cnt+1)){
					return 1;
				}
				vis[i]=0;
				return 0;
			}
		}
	}
	return 0;
}

int main(){
	while(~scanf("%d",&n),n){
		memset(vis,0,sizeof(vis));
		int m=0;
		sum=0;
		for(int i=0;i<n;i++){
			scanf("%d",&a[i]);
			sum+=a[i];
			m=max(m,a[i]);
		}
		sort(a,a+n,cmp);
		int flag=0;
		for(int l=m;l<=sum/2;l++){
			while(a[0]>l||sum%l)l++;
			if(dfs(l,0,0,0)){
				printf("%d\n",l);
				flag=1;
				break;
			}
		}
		if(!flag)printf("%d\n",sum);
	}
}


猜你喜欢

转载自blog.csdn.net/running_acmer/article/details/80549970