【洛谷】P1120 小木棍 [数据加强版]-搜索

传送门:洛谷P1120


题解

先贴一篇很好的博客:题解 P1120 【小木棍 [数据加强版]】-Zap
一些剪枝:

dfs(int k,int last,int rest)
k表示正在拼第几根原来的长棍,last表示使用的上一根木棍,rest表示当前
在拼的长棍还有多少长度未拼。

关于长度:
1.枚举总长度sum [maxlen,sum]之间的sum的约数
2.枚举到sum/2即可,再大只能是sum了

关于木棍:
1.优先填长的木棍
2.当用木棍i拼合长棍时(last=i),从第i+1根木棍开始往后搜
3.dfs返回拼接失败时,更换当前使用的木棍时直接跳到下一个长度严格
小于该木棍的木棍。(预处理next)
4.二分找第一个长度<=rest的木棍

关于直接回溯:
1.当前长棍剩余的未拼长度等于当前木棍的长度(剩下的较短的木棍都无法组成几根长棍,换成长棍更不可能)
2.当前长棍剩余的未拼长度等于原始长度(怎么都不可能了)

关于vs数组:
回溯时清0就不用memset了。

搜索(剪枝)是基本功,要练好啊


代码

#include
using namespace std;

int n,m,a[70],sum,len,cot,vs[70],nxt[70];

inline int fd(int r,int x)
{
	int l=1,re=0,mid;
	for(;l<=r;){
		mid=(l+r)>>1;
		if(a[mid]<=x) l=(re=mid)+1;
		else r=mid-1;
	}
	return re;
}

bool dfs(int k,int last,int rest)
{
	if(!rest){
		if(k==cot) return true;
		int i;
		for(i=n;i;--i) if(!vs[i]) break;
		vs[i]=1;
		if(dfs(k+1,i,len-a[i])){vs[i]=0;return true;}
		else{vs[i]=0;return false;}
	}
	int i=fd(last-1,rest);
	for(;i;--i) if(!vs[i]){
		vs[i]=1;
		if(dfs(k,i,rest-a[i])){vs[i]=0;return true;
		}else if(a[i]==rest || rest==len){vs[i]=0;return false;}
		vs[i]=0;i=nxt[i];if(i==1) break;
	}
	return false;
}

int main(){
	int i,j;
	scanf("%d",&n);
	for(i=1;i<=n;++i){
		scanf("%d",&j);
		if(j>50) continue;
		a[++m]=j;sum+=j;
	}
	n=m;sort(a+1,a+n+1);nxt[1]=1;
	for(i=2;i<=n;++i) nxt[i]= a[i]==a[i-1]?nxt[i-1]:i;
	for(len=a[n];len+len<=sum;++len)
	  if(sum%len==0){
	  	cot=sum/len;vs[n]=1;
	  	if(dfs(1,n,len-a[n])) break;
	  	vs[n]=0;
	  }
	if(len+len>sum) len=sum;
	printf("%d\n",len);
    return 0;
}

你可能感兴趣的:(暴力)