[算进] 小木棍

Problem

ACwing 题目地址

洛谷 题目地址

Before

做完16*16的数独后感觉这些搜索题都是小清新。

但是小木棍这题还是烦了我特别久,因此来写篇题解吧,总结一下。

Sulotion

这题无非就是两个剪枝,优化搜索顺序 + 去掉等效状态

优化搜索顺序

  • 1.很明显,从大到小排,这样未来可选择的空间更少,搜索树更小。

去掉等效状态

  • 1.假设一条木棍是由 1,3,6 拼接成的,1,6,3 和 3,1,6 这些顺序就是等效状态。我们只要保证每根大木棍内部编号顺序递增,那么就可以剪掉这些等效状态。

  • 2.假设用长度为 5 的木棍拼接失败,那么就要跳过所以长度为 5 的木棍。所以跳过长度相等的并且失败了的木棍

  • 3.如果第一个木棍拼接失败,那么可以剪掉这个分支。反证法:条件:第一个木棍拼接失败。假设这个这个分支可以成功,那么将第一根木棍所在的那条大木棍放在第一个木棍这里,就可以拼接成功,条件不成立,故原命题成立。如果最后一个木棍拼接失败,那么可以剪掉这个分支,因为最后一个木棍是恰好可以拼好这个大木棍的,它的位置必须在这里,如果不行的话就说明这里不能成功拼接。

Code

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
using namespace std;
inline int read() {
	int x=0,f=1; char ch=getchar();
	while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
	return x * f;
}
const int N = 107;
int n,sum,len,ans;
int sticks[N];
bool vis[N];
bool cmp(int x,int y) {
	return x > y;
}
bool Dfs(int tot,int now,int start) {	//保证每根木棍内元素递增 
	if(tot*len == sum) return true;
	if(now == len) return Dfs(tot+1,0,0);
	for(int i=start;i<=n;++i) {
		if(vis[i]) continue;
		if(now+sticks[i] <= len) {
			vis[i] = 1;
			if(Dfs(tot,now+sticks[i],i+1)) return true;
			vis[i] = 0;
			if(!now) return false;	//这是第一个木棍,
			if(now+sticks[i] == len) return false; //这是最后一根木棍
			int j = i;
			while(j<=n && sticks[j]==sticks[i]) ++j;
			i = j - 1;	//跳过相同木棍 
		}
	}
	return false;
}
void work() {
	memset(vis, 0, sizeof(vis)); sum = 0; ans = -1;
	for(int i=1;i<=n;++i) {
		sticks[i] = read();
		sum += sticks[i];
	}
	sort(sticks+1, sticks+1+n, cmp);
	for(int i=1;i<=sum;++i) {
		if(sum%i == 0) {
			len = i;
			if(Dfs(0,0,0)) {
				ans = i; break;
			}
		}
	}
	printf("%d
",ans);
}
int main()
{
	while(true) {
		n = read();
		if(n == 0) break;
		work();
	}
	return 0;
}

Summary

  • 看到搜索题第一步想优化枚举顺序

  • 第二步剪去等效状态

原文地址:https://www.cnblogs.com/BaseAI/p/12148955.html