2019.12.08日常总结兼剪枝讲解

前言

无论在什么地方,总有一类题目,我们很难想到正解,为了得分,我们不得不使用暴力或者搜索的方法骗分,异或正解就是暴力或者搜索。我们都知道,原始的搜索效率极低,很多情况下无法通过题目。


剪枝

我们也知道,搜索的过程相当于程序在遍历搜索树的过程,有些时候,我们可以提前知道搜索树上某些枝条上一定无解,此时,我们就没有必要浪费时间在这些枝条上进行搜索,相当于把它给剪去,这就是剪枝

剪枝的三大原则:正确、准确、高效。其中,正确性是最最重要的,如果你把正解给剪掉了,一切白搭!

剪枝可以分为可行性剪枝最优性剪枝。常见的方法有:控制上下界排除无效解记忆化排除等效冗余等。

剪枝的副作用是让程序的时间复杂度变得无法计算。而剪枝的题目没有什么技巧,只有多想多刷


例题

洛谷P2383

【题目】: 现给出一些木棒长度,那么能否用给出的木棒(木棒全用完)组成一个正方形呢?
【思路】: 本题有以下几个剪枝:
( 1 ) (1) 如果木棒长度总和 s u m sum 不是 4 4 的倍数,一定是输出no
( 2 ) (2) 把木棒分成四类,每一类放在一条边上,则如果某一类的木棒长度总和 > s u m 4 > \frac{sum}{4} ,一定无解。
当然了,如果加上快读读优)肯定可以更快哦!
【代码】:

int a[25],sum,test_number,n;
bool dfs(int k,int s1,int s2,int s3,int s4){
	if (k>n){
		if (s1==s2&&s2==s3&&s3==s4)
			return true;
		else return false;
	}
	if (s1+a[k]<=sum/4){//剪枝2:限制搜索条件
		if (dfs(k+1,s1+a[k],s2,s3,s4))
			return true;
	}
	if (s2+a[k]<=sum/4){
		if (dfs(k+1,s1,s2+a[k],s3,s4))
			return true;
	}
	if (s3+a[k]<=sum/4){
		if (dfs(k+1,s1,s2,s3+a[k],s4))
			return true;
	}
	if (s4+a[k]<=sum/4){
		if (dfs(k+1,s1,s2,s3,s4+a[k]))
			return true;
	}
	return false;
}
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
	char c=0;int x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}
int main(){
	test_number=read();
	while (test_number--){
		n=read();sum=0;
		for(int i=1;i<=n;i++){
			a[i]=read();
			sum+=a[i];
		}
		if (sum%4)
			printf("no\n");
		else{
			if (dfs(1,0,0,0,0))
				printf("yes\n");
			else printf("no\n");
		}
	}
	return 0;
}

洛谷P1025

【题目】:
将整数 n n 分成 k k 份,且每份不能为空,任意两个方案不相同(不考虑顺序)。
例如: n = 7 n=7 k = 3 k=3 ,下面三种分法被认为是相同的。
1 , 1 , 5 1,1,5
1 , 5 , 1 1,5,1
5 , 1 , 1 5,1,1
问有多少种不同的分法。
【思路】: 因为解不考虑顺序,所以我们可以将划分出来的数从小到大排序搜索,即设 A A 为解,则一定保证 A i 1 A i A_{i-1} \leq A_i
因此有一个上下界剪枝,即设将 n n 分解成 A 1 + A 2 + A 3 + . . . + A i 1 A_1+A_2+A_3+...+A_{i-1} ,则 A i 1 A i n A 1 A 2 A 3 . . . A i 1 k i + 1 A_{i-1} \leq A_i \leq \frac{n-A_1-A_2-A_3-...-A_{i-1}}{k-i+1}
当然,这样虽然可以AC,但仍然可以优化,即加上记忆化。设 f n , k , l a s t f_{n,k,last} 表示把 n n 分为 k k 分,每份数都 l a s t \geq last 的方案数。
【代码】:

int f[210][10][210],n,k;
int dfs(int n,int k,int last){
	if (n==0) return 1;
	if (k==0) return 0;
	if (f[n][k][last]!=-1)
		return f[n][k][last];
	f[n][k][last]=0;
	for(int i=last;i<=n/k;i++)
		f[n][k][last]+=dfs(n-i,k-1,i);
	return f[n][k][last];
}
int main(){
	scanf("%d%d",&n,&k);
	memset(f,-1,sizeof(f));
	printf("%d",dfs(n,k,1));
	return 0;
}
发布了82 篇原创文章 · 获赞 4 · 访问量 1778

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/103444141