IDDFS与IDA*算法

IDDFS(迭代加深搜索)

IDDFS是Iterative Deepening DFS的缩写,是一种结合了DFS和BFS思想的搜索方法。具体操作为:设置每次DFS的深度depth,开始深度为1,每当没找到答案则depth++继续DFS,直到找到了答案。

每一层的广度上有着BFS的思想,但是实现是用DFS。

习题:埃及分数 LibreOJ - 10022

题意:给定一对分子分母a和b,求用单位分数来表示分数 a b \frac{a}{b} ba,单位分数即分子为1的分数。要求单位分数的个数最小,且分母不可相同,如果有多解,要求最小的分数是多组解中最大的。

题目没有明显的界限,那先想到我们第一层可以从 1 2 \frac{1}{2} 21开始,下一层 1 3 \frac{1}{3} 31,然后每一层分母都+1来进行尝试,对于同层也是分母+1然后向下搜索。显然这样的搜索十分盲目,有过多的浪费,因此我们必须得自己设定一定的搜索界限。

对于搜索下限:
假设到当前位置,剩余的分数为 x y \frac{x}{y} yx,设下一个从 1 s \frac{1}{s} s1开始遍历,显然要求 x y ≥ 1 s \frac{x}{y}\ge\frac{1}{s} yxs1 即有 s ≥ x y s\ge\frac{x}{y} syx
另外考虑到分母不能相同,而且是从大向小的搜索,还要考虑上一个选择的分母+1是不是求得的s更大,如果更大则更新为上次分母+1。

对于搜索上限:
根据搜索的深度,我们可以求一个数k表示还能选几个分母,用e来表示上限,那么要满足 k e > x y \frac{k}{e}>\frac{x}{y} ek>yx(k>1),得到 e < k y x e< \frac{ky}{x} e<xky

借用这两个限制就很容易求得答案了
注意:这题的数据大概率会爆int

#include<cstdio>
using namespace std;
int n, pos, val[1010];//最多不超过1000层,val记录每一层得到的结果
bool ida(int now, int dep) {
	if (now > dep)return false;
	if (val[pos] << (dep - now) < n)return false;
	if (val[pos] == n)return true;
	pos++;
	for (int i = 0; i < pos; i++) {
		val[pos] = val[pos - 1] + val[i];
		if (ida(now + 1, dep))return true;//相加
		val[pos] = val[pos - 1] - val[i];
		if (ida(now + 1, dep))return true;//相减
	}
	pos--;//失败则退回一步
	return false;
}
int main(void) {
	while (~scanf("%d", &n) && n) {
		for (int dep = 1;; dep++) {
			val[pos = 0] = 1;
			if (ida(0, dep)) {//开始为0层
				printf("%d\n", pos);
				break;
			}
		}
	}
	return 0;
}
IDA*算法

IDA*算法能简要概述为:IDA* = IDDFS + 估价函数
在这里估价函数的作用是判断在某个深度时问题是否可解,若无解则返回,即IDDFS利用估价函数进行剪枝操作。

例题:Power Calculus POJ - 3134
题意:给定一个数n,让你从1开始进行加减,可以利用所有已有的结果,求最少步数得到n。

根据罗书上的说法:
IDDFS:指定递归深度,递归不超过这个深度。
估价函数:如果最快的方式计算(不断乘2),看当前值在规定深度的最大值是否小于n。

书上代码如下:

#include<cstdio>
using namespace std;
int n, pos, val[1010];//最多不超过1000层,val记录每一层得到的结果
bool ida(int now, int dep) {
	if (now > dep)return false;
	if (val[pos] << (dep - now) < n)return false;
	if (val[pos] == n)return true;
	pos++;
	for (int i = 0; i < pos; i++) {
		val[pos] = val[pos - 1] + val[i];
		if (ida(now + 1, dep))return true;//相加
		val[pos] = val[pos - 1] - val[i];
		if (ida(now + 1, dep))return true;//相减
	}
	pos--;//失败则退回一步
	return false;
}
int main(void) {
	while (~scanf("%d", &n) && n) {
		for (int dep = 1;; dep++) {
			val[pos = 0] = 1;
			if (ida(0, dep)) {//开始为0层
				printf("%d\n", pos);
				break;
			}
		}
	}
	return 0;
}

①习题:DNA sequence HDU - 1560

粗略的写写草稿发现贪心是不可行的。

然后想到4^len来构建字符串,如果盲目构造能有无穷多的结果,因此可以用到IDDFS,规定层数,设置其值开始为最长的字符串的长度。

接下来是构造h函数用于剪枝,h函数要求的是理想中的最优值,可以想到每个字符串中未匹配的最长长度即可为理想中的最优值,如果剩余可以构造的长度小于该值则返回。

#include<cstdio>
#include<string>
#include<iostream>
#include<algorithm>
using namespace std;
const char ch[] = "ACGT";
int t, n, cnt[10];
string dna[10];
int h() {
	int res = 0;
	for (int i = 0; i < n; i++) 
		res = max(res, (int)dna[i].length() - cnt[i]);
	return res;
}
bool ida(int now, int depth) {
	if (now == depth + 1) {
		if (!h())return true;
		else return false;
	}
	if (depth - now + 1 < h())
		return false;//当前这一层还没选,剩下可选的有depth-now+1个								
	int tmp[10] = { 0 };
	for (int i = 0; i < n; i++)
		tmp[i] = cnt[i];
	for (int i = 0; i < 4; i++) {
		int flag = 0;
		for (int j = 0; j < n; j++)
			if (dna[j][cnt[j]] == ch[i])
				cnt[j]++, flag = 1;
		if (flag) {
			if (ida(now + 1, depth))return true;
			else {
				for (int j = 0; j < n; j++)
					cnt[j] = tmp[j];
			}
		}
	}
	return false;
}
int main(void) {
	scanf("%d", &t);
	while (t--){
		scanf("%d", &n);
		int st = 0;
		for (int i = 0; i < n; i++) {
			cin >> dna[i];
			if (dna[i].length() > st)
				st = dna[i].length();
			cnt[i] = 0;
		}
		int ans = 0;
		for (int depth = st;; depth++) {
			if (ida(1, depth)) {
				ans = depth; break;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

②习题 The Rotation Game HDU - 1667

很显然像这种可以无限递归的能考虑IDA*

同样IDDFS设置每次的最大深度,从1开始。

H函数要考虑最理想的步数是多少,观察相邻的3个块,如122的形式,这种两个连续相同,假设其他5个块都匹配了2,那么最少1步即可;如121,123这种,两边和中间的不同,那必定得至少移动两次。因此构造出了一个比较低效的H函数,获得的值只有0,1,2三种情况。

万幸的是13s低空水过~,其实下列代码还能进行适当的常数优化,还有进步空间。

#include<cstdio>
#include<algorithm>
using namespace std;
int row1[7], row2[7], col1[7], col2[7];
int rowh1, rowh2, colh1, colh2, pos;
char ans[1000];
void print() {//测bug用的
	printf("\n  %d %d\n", col1[colh1], col2[colh2]);
	printf("  %d %d\n", col1[(colh1 + 1) % 7], col2[(colh2 + 1) % 7]);
	for (int i = 0; i < 7; i++)printf("%d", row1[(rowh1 + i) % 7]);
	printf("\n  %d %d\n", col1[(colh1 + 3) % 7], col2[(colh2 + 3) % 7]);
	for (int i = 0; i < 7; i++)printf("%d", row2[(rowh2 + i) % 7]);
	printf("\n  %d %d\n", col1[(colh1 + 5) % 7], col2[(colh2 + 5) % 7]);
	printf("  %d %d\n\n", col1[(colh1 + 6) % 7], col2[(colh2 + 6) % 7]);
}
int getH() {
	int res = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
	if (row1[(rowh1 + 3) % 7] != row1[(rowh1 + 2) % 7])t1++;
	if (row1[(rowh1 + 3) % 7] != row1[(rowh1 + 4) % 7])t1++;
	res = max(res, t1);
	if (row2[(rowh2 + 3) % 7] != row2[(rowh2 + 2) % 7])t2++;
	if (row2[(rowh2 + 3) % 7] != row2[(rowh2 + 4) % 7])t2++;
	res = max(res, t2);
	if (col1[(colh1 + 3) % 7] != col1[(colh1 + 2) % 7])t3++;
	if (col1[(colh1 + 3) % 7] != col1[(colh1 + 4) % 7])t3++;
	res = max(res, t3);
	if (col2[(colh2 + 3) % 7] != col2[(colh2 + 2) % 7])t4++;
	if (col2[(colh2 + 3) % 7] != col2[(colh2 + 4) % 7])t4++;
	res = max(res, t4);
	return res;
}
bool ida(int now,int depth) {
	//printf("last=%c,pos=%d,now=%d,dep=%d,getH=%d\n", ans[pos - 1], pos, now, depth, getH());
	//print();
	if (getH() > depth - now)return false;
	if (now == depth) {
		if (!getH())return true;
		else return false;
	}
	int tmp1, tmp2;
	ans[pos++] = 'A';
	colh1 = (colh1 + 1) % 7;
	tmp1 = row1[(rowh1 + 2) % 7];
	tmp2 = row2[(rowh2 + 2) % 7];
	row1[(rowh1 + 2) % 7] = col1[(colh1 + 2) % 7];
	row2[(rowh2 + 2) % 7] = col1[(colh1 + 4) % 7];
	if (ida(now + 1, depth))return true;
	else {
		pos--; colh1 = (colh1 + 6) % 7;
		row1[(rowh1 + 2) % 7] = tmp1;
		row2[(rowh2 + 2) % 7] = tmp2;
	}

	ans[pos++] = 'B';
	colh2 = (colh2 + 1) % 7;
	tmp1 = row1[(rowh1 + 4) % 7];
	tmp2 = row2[(rowh2 + 4) % 7];
	row1[(rowh1 + 4) % 7] = col2[(colh2 + 2) % 7];
	row2[(rowh2 + 4) % 7] = col2[(colh2 + 4) % 7];
	if (ida(now + 1, depth))return true;
	else {
		pos--; colh2 = (colh2 + 6) % 7;
		row1[(rowh1 + 4) % 7] = tmp1;
		row2[(rowh2 + 4) % 7] = tmp2;
	}

	ans[pos++] = 'C';
	rowh1 = (rowh1 + 6) % 7;
	tmp1 = col1[(colh1 + 2) % 7];
	tmp2 = col2[(colh2 + 2) % 7];
	col1[(colh1 + 2) % 7] = row1[(rowh1 + 2) % 7];
	col2[(colh2 + 2) % 7] = row1[(rowh1 + 4) % 7];
	if (ida(now + 1, depth))return true;
	else {
		pos--; rowh1 = (rowh1 + 1) % 7;
		col1[(colh1 + 2) % 7] = tmp1;
		col2[(colh2 + 2) % 7] = tmp2;
	}

	ans[pos++] = 'D';
	rowh2 = (rowh2 + 6) % 7;
	tmp1 = col1[(colh1 + 4) % 7];
	tmp2 = col2[(colh2 + 4) % 7];
	col1[(colh1 + 4) % 7] = row2[(rowh2 + 2) % 7];
	col2[(colh2 + 4) % 7] = row2[(rowh2 + 4) % 7];
	if (ida(now + 1, depth))return true;
	else {
		pos--; rowh2 = (rowh2 + 1) % 7;
		col1[(colh1 + 4) % 7] = tmp1;
		col2[(colh2 + 4) % 7] = tmp2;
	}

	ans[pos++] = 'E';
	colh2 = (colh2 + 6) % 7;
	tmp1 = row1[(rowh1 + 4) % 7];
	tmp2 = row2[(rowh2 + 4) % 7];
	row1[(rowh1 + 4) % 7] = col2[(colh2 + 2) % 7];
	row2[(rowh2 + 4) % 7] = col2[(colh2 + 4) % 7];
	if (ida(now + 1, depth))return true;
	else {
		pos--; colh2 = (colh2 + 1) % 7;
		row1[(rowh1 + 4) % 7] = tmp1;
		row2[(rowh2 + 4) % 7] = tmp2;
	}

	ans[pos++] = 'F';
	colh1 = (colh1 + 6) % 7;
	tmp1 = row1[(rowh1 + 2) % 7];
	tmp2 = row2[(rowh2 + 2) % 7];
	row1[(rowh1 + 2) % 7] = col1[(colh1 + 2) % 7];
	row2[(rowh2 + 2) % 7] = col1[(colh1 + 4) % 7];
	if (ida(now + 1, depth))return true;
	else {
		pos--; colh1 = (colh1 + 1) % 7;
		row1[(rowh1 + 2) % 7] = tmp1;
		row2[(rowh2 + 2) % 7] = tmp2;
	}

	ans[pos++] = 'G';
	rowh2 = (rowh2 + 1) % 7;
	tmp1 = col1[(colh1 + 4) % 7];
	tmp2 = col2[(colh2 + 4) % 7];
	col1[(colh1 + 4) % 7] = row2[(rowh2 + 2) % 7];
	col2[(colh2 + 4) % 7] = row2[(rowh2 + 4) % 7];
	if (ida(now + 1, depth))return true;
	else {
		pos--; rowh2 = (rowh2 + 6) % 7;
		col1[(colh1 + 4) % 7] = tmp1;
		col2[(colh2 + 4) % 7] = tmp2;
	}

	ans[pos++] = 'H';
	rowh1 = (rowh1 + 1) % 7;
	tmp1 = col1[(colh1 + 2) % 7];
	tmp2 = col2[(colh2 + 2) % 7];
	col1[(colh1 + 2) % 7] = row1[(rowh1 + 2) % 7];
	col2[(colh2 + 2) % 7] = row1[(rowh1 + 4) % 7];
	if (ida(now + 1, depth))return true;
	else {
		pos--; rowh1 = (rowh1 + 6) % 7;
		col1[(colh1 + 2) % 7] = tmp1;
		col2[(colh2 + 2) % 7] = tmp2;
	}

	return false;
}
int main(void) {
	while (~scanf("%d", &col1[0]) && col1[0]){
		scanf("%d %d %d", &col2[0], &col1[1], &col2[1]);
		for (int i = 0; i < 7; i++)scanf("%d", &row1[i]);
		col1[2] = row1[2]; col2[2] = row1[4];
		scanf("%d %d", &col1[3], &col2[3]);
		for (int i = 0; i < 7; i++)scanf("%d", &row2[i]);
		col1[4] = row2[2]; col2[4] = row2[4];
		scanf("%d %d %d %d", &col1[5], &col2[5], &col1[6], &col2[6]);
		rowh1 = rowh2 = colh1 = colh2 = pos = 0;
		int dep = 0;
		while (!ida(0, dep))dep++;
		if (dep)for (int i = 0; i < dep; i++)printf("%c", ans[i]);
		else printf("No moves needed");
		printf("\n%d\n", row1[(rowh1 + 2) % 7]);
	}
	return 0;
}

总结

  总的来说,A*类算法其实就是在遍历的过程中加入合适的剪枝,从而引导遍历方向向着离答案近的方向进行,因此说白了就是一种剪枝法,但是这类算法有一定的模式可套,即每次以最理想的情况来判断是否剪枝,更容易想到代码的实现方式。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JILIN.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值