小木棍

一个搜索剪枝。

题目传送门 P1120 小木棍

题目不在此描述。不好评价。

这道题里面要带 \(depth,w,last\) 三个参数,其中 \(depth\) 表示搜索到的现有的待拼接木棍序号,其中如果有 \(depth>cnt\) ,那么 return true 跳出询问。\(w\) 表示当前木棍已经拼接的长度,如 \(w=len\) 时就可以跳转至 \(depth+1\) 的木棍处完成拼接。 \(last\) 表示上一次拼接的小木棍序号。\(last\) 具体用途见剪枝。

初始化 init: 对输入的每一个 \(a[i]\) 进行排序,得到一个单调不升的序列,然后再进行操作。

其次考虑以下的三种剪枝:

  1. 控制上一个搜索到的小木棍和现在所扫描到的小木棍关系是一定不升的,这样就能够控制搜索到完全相同的局面。

证明: \(w-x-y = w-(x+y) = w-(y+x) = w-y-x\) .

具体实现:因为先前已经将序列 \(\{a\}\) 排序完成,所以只需要记录 \(last\) 然后并让现木棍 \(a[i]\)\(i>last\) 即可。

  1. 记录上一次搜索失败的木棍,记作 \(mark\) 。 当 \(a[i]=mark\) 时直接执行跳出语句。

  2. 如果第一根木棒就以失败返回,那么可以得出结论: 该木棍无论放在哪里均为无效。所以可以决策出这样的 \(dfs\) 分支无效,所以可以剪掉。

然后就是漫长的卡常之路,最后最快速度 \(245ms\) 过去。

代码如下。

#include <bits/stdc++.h>
#define ll long long
const int N=70;
int L,R,n,cnt;int a[N];
bool vis[N];
int len;

bool dfs(int dep,int la,int w){
	if(dep>cnt) return true;
	if(w==len) return dfs(dep+1,1,0);
	int mark=0;
	for(register int i=la;i<=n;++i){
		if(!vis[i] && w+a[i]<=len || mark!=a[i]){
			vis[i] = true;
			if(dfs(dep,i+1,w+a[i]))return true;
			mark=a[i];
			vis[i] = false;
			if(!w || w+a[i]==len) return false;
		}
	}
	return false;
}

int main(){
	scanf("%d",&n);
	for(register int i=1;i<=n;++i){
		scanf("%d",a+i);
		if(a[i]>50){
			a[i]=0;
			n--,i--;
		}
		L=std::max(L,a[i]);
		R+=a[i];
	} 
	std::sort(a+1,a+n+1);
	std::reverse(a+1,a+n+1);
	for(len=L;len<=R;++len){
		if(R%len)continue;
		cnt=R/len;
		if(dfs(1,1,0)){
			printf("%d\n",len);return 0;
		}	
	}
	puts("None");
	return 0;
}
posted @ 2023-10-01 11:21  q(x)  阅读(23)  评论(0)    收藏  举报