【迭代加深iddfs】笔记

麻了,整了一下午的送礼物,人都废了

写篇水贴,休息一下吧

4. 迭代加深

迭代加深,简写为iddfs,是一种特殊的dfs,其特殊在于每一次搜索都限定了搜索树的深度

这有什么用呢?

对于某些题而言,如果使用普通的dfs,可能会因为该搜索树无穷大而T飞;如果使用普通的bfs,空间可能会炸飞,而且每一个阶段的状态难以定义(比如,如果使用普通的bfs完成骑士精神,你打算怎么定义其状态)

而iddfs,则是用dfs的形式完成bfs的功能,空间复杂度与dfs类似,时间复杂度与bfs类似,非常的优秀

从理论上来讲,iddfs和dfs的写法几乎一样,只是多了一个限定的深度的判断

比如:

//iddfs内部:
if(x>depth){//如果已经超出了当前深度,直接退出
	return ;
}
//主函数
iddfs(x);//迭代加深搜索
if(flag){//找到了答案
	//输出答案
}
depth++;//放大限制的层数

所以,直接上例题罢

4.1. 实战演练

Eg:Addition Chains 加成序列

一句话题意:

现有一长度为 m m m 的数列 a a a,且数列 a a a 有如下性质: a 1 = 1 , a m = n , ∀   k  满足  a k = a i + a j ( 0 ≤ i , j ≤ k − 1 , i , j  可以相等 ) a_1=1,a_m=n,\forall\ k\ \text{满足}\ a_k=a_i+a_j(0\le i,j\le k-1,i,j\ \text{可以相等}) a1=1,am=n, k 满足 ak=ai+aj(0i,jk1,i,j 可以相等)
给定 n n n,求在 m m m 最小的情况下,字典序最大的 a a a 数列

显然,由于无法确定 m m m,所以普通的dfs显然T飞

因此,我们可以使用迭代加深,限制 m m m 的大小,这样,我们也就保证了最先搜出来的数列一定保证 m m m 最小

然后,就是常规的搜索

注意几个剪枝点:

  1. 由数列 a a a 的性质得,该数列一定是递增的,所以可以根据这一点来进行剪枝
  2. 如果新得到的数大于 n n n,也可以直接停止了

其实都是很显然的

另外,为了构造字典序最大的序列,我们可以使每一次的 a i , a j a_i,a_j ai,aj 在合法情况下取尽可能大的值,以构造最大的字典序

因此,我们很快就能愉快的打出代码:

#include<cstdio>
int n,depth;
bool flag;
int now[25],ans[25];
void dfs(int x){
	if(x>depth){
		return ;
	}
	for(int i=x-1;i>=1;i--){
		for(int j=i;j>=1;j--){//枚举加数a_i,a_j,得到a_k
			if(now[i]+now[j]>n){//通过剪枝2剪枝
				continue;
			}
			if(now[i]+now[j]<now[x-1]){//通过剪枝1剪枝
				break;
			}
			now[x]=now[i]+now[j];
			if(now[x]==n){//已经得到了答案
				for(int i=1;i<=depth;i++){//为输出做准备
					ans[i]=now[i];
				}
				flag=1;//标记,已经有了答案
				return ;
			}
			dfs(x+1);
			if(flag){//如果我们已经找到了答案,直接退出
				return ;
			}
		}
	}
}
int main(){
	now[1]=ans[1]=1,now[2]=ans[2]=2;//初始化,用于特判
	while(1){
		scanf("%d",&n);
		if(!n){
			return 0;
		}
		if(n==1){//1,2特判,但是2不特判好像也行?
			printf("1\n");
			continue;
		}else if(n==2){
			printf("1 2\n");
			continue;
		}
		for(depth=3;depth<=20;depth++){//迭代加深
			dfs(3);
			if(flag){//找到答案就输出
				for(int i=1;i<=depth;i++){
					printf("%d ",ans[i]);
				}
				printf("\n");
				break;
			}
		}
		flag=0;//初始化
		for(int i=3;i<=depth;i++){
			now[i]=ans[i]=0;
		}
	}
	return 0;
}

但是,很可惜,还是T了

其实,这里还有一个至关重要的优化:

假设当前我们已经构造了 a k a_k ak,限制的长度为 m 1 m_1 m1

思考:如果我们不考虑合法性,怎样使 a k + 1 a_{k+1} ak+1 最大化?

显然,因为 a k a_k ak 使当前序列里的最大值,所以, max ⁡ { a k + 1 } = 2 × a k \max\{a_{k+1}\}=2\times a_k max{ak+1}=2×ak

同理, max ⁡ { a k + 2 } = 2 × a k + 1 \max\{a_{k+2}\}=2\times a_{k+1} max{ak+2}=2×ak+1

根据这样的规律,我们就可以得出在当前的 a k a_k ak 下, a m 1 a_{m_1} am1 的最大值

如果 a m 1 < n a_{m_1}<n am1<n,说明什么?

说明在当前情况下,即使每一步都选择最优方法,都不能构造出一组可行解

既然如此,我们就可以提前去减掉 a k a_k ak,以避免时间的浪费

如上的过程,我们可以写作一个估价函数,这里由于实现简单,故蒟蒻为写成函数

BTW,如果我们在普通的dfs中整一个估价函数,这个dfs就升级为A*,如果我们在iddfs中整一个估价函数,这个dfs就升级为IDA*

现在,我们就可以得到AC代码了

#include<cstdio>
int n,depth,now[25],ans[25],flag;
void dfs(int x){
	if(flag){
		return ;
	}
	if(x>depth){
		return ;
	}
	for(int i=x-1;i>=1;i--){
		for(int j=i;j>=1;j--){
			if(now[i]+now[j]>n){
				continue;
			}
			if(now[i]+now[j]<now[x-1]){
				break;
			}
			int sum=now[i]+now[j];//上文所说的估价函数进行优化
			for(int k=x+1;k<=depth;k++){
				sum*=2;
			}
			if(sum<n){
				break;
			}
			now[x]=now[i]+now[j];
			if(now[x]==n){
				for(int i=1;i<=depth;i++){
					ans[i]=now[i];
				}
				flag=1;
				return ;
			}
			dfs(x+1);
			if(flag){
				return ;
			}
		}
	}
}
int main(){
	now[1]=ans[1]=1,now[2]=ans[2]=2;
	while(1){
		scanf("%d",&n);
		if(!n){
			return 0;
		}
		if(n==1){
			printf("1\n");
			continue;
		}else if(n==2){
			printf("1 2\n");
			continue;
		}
		for(depth=3;depth<=20;depth++){
			dfs(3);
			if(flag){
				for(int i=1;i<=depth;i++){
					printf("%d ",ans[i]);
				}
				putchar('\n');
				break;
			}
		}
		flag=0;
		for(int i=3;i<=depth;i++){
			now[i]=ans[i]=0;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值