IDA*

A* 和 IDA* 听起来高大上

简单来说就是搜索的一个优化,通过一个估值函数让搜索不往不必要的地方发展

A*是用在BFS上的

IDA*是用在DFS上的

A*=优先队列+估价函数

IDA=迭代加深+估价函数

如果当前深度+估计还要的深度>限制深度 那就直接return 了

估价函数要区好,要尽量接近实际深度且不超过实际深度


迭代加深

例题 埃及分数

描述

在古埃及,人们使用单位分数的和(形如1/a的, a是自然数)表示一切有理数。如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的。对于一个分数a/b,表示方法有很多种,但是哪种最好呢?首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越好。

如:19/45=1/3 + 1/12 + 1/180
19/45=1/3 + 1/15 + 1/45
19/45=1/3 + 1/18 + 1/30,
19/45=1/4 + 1/6 + 1/180
19/45=1/5 + 1/6 + 1/18. 
最好的是最后一种,因为1/18比1/180,1/45,1/30,1/180都大。

给出a,b(0<a<b<1000),编程计算最好的表达方式。

输入:a b
输出:若干个数,自小到大排列,依次是单位分数的分母。

样例1

样例输入1

19 45

样例输出1

5 6 18

1. 1/n=1/(n+1)+1/n*(n+1) (小学奥数)

所以对于答案,深度是无法得知的,因为可以永远拆分下去

就是说,你不知道答案由多少个分数组成

2.题目要求用最少的个数来表示分数

这对应搜索树中最小的层数


这也对应了迭代加深的条件

1.它搜索时可能会达到极大的深度,而这样的答案是没用且费时的

2.它是个最优解问题,最优的答案深度最小

于是我们可以限制搜索层数,本题也就是限制分数的个数

我们从1开始枚举深度,当有解时就直接退出(因为一定是最优解)

本题,深度为1,2都无解,当为3时,最优解为5 6 18

当同一深度有多个解时,判断一下就好了

当然,当深度为d时,会把1-d-1层重新搜索一遍

但对于第d层新增的节点,1-d-1层的就显得微不足道了

我们要保证后面搜的分母比前面的都大,不然会乱
int get_ans(int d,int from,int aa,int bb){//层数,最小的分母从from开始,当前分子,分母
	if(d==maxd){//maxd 为限制的层数
		if(bb%aa) return false;//最后剩下的分数要满足分子为1
		v[d]=bb/aa;//v[i]是当前答案,ans是最终答案
		if(better(d)){//判断同一层的最优解
			for(int i=0;i<=d;i++) ans[i]=v[i];
		}
		return true;
	}
	from=max(from,bb/aa+1);//1/c<aa/bb,c>bb/aa,c>=bb/aa+1
	int ok=0;
	for(int i=from;;i++){//从from开始向上枚举
		if(aa*i>=bb*(maxd-d+1)) break;
                //aa/bb>=i/(maxd-d+1)后面分母一定比i大,如果全是i都不行肯定不行
		v[d]=i;//记录答案
		int b2=bb*i,a2=aa*i-bb,g=gcd(a,b);//通分并约分
		if(get_ans(d+1,i+1,a2/g,b2/g)) ok=1;
	}
	return ok;
}

估价函数

对未来至少的层数做一个保守的估计

例题

输入格式:

 

第一行有一个正整数T(T<=10),表示一共有N组数据。接下来有T个5×5的矩阵,0表示白色骑士,1表示黑色骑士,*表示空位。两组数据之间没有空行。

 

输出格式:

 

对于每组数据都输出一行。如果能在15步以内(包括15步)到达目标状态,则输出步数,否则输出-1。

输入样例#1:

2
10110
01*11
10111
01001
00000
01011
110*1
01110
01010
00100

输出样例#1: 

7
-1

 

1.未知多少层

2.寻找最优解

3.层数只用枚举到15

 4.考虑估价函数,如果当前状态与最终状态的不匹配的格子有x个,那么至少需要x-1次复原

于是很容易写出,h为估价函数

int h(){
	int ret=0;
	for(int i=0;i<5;i++)
		for(int j=0;j<5;j++)
			if(Map[i][j]!=goal[i][j]) ret++;
	return ret;
}
if(h()+d-1>maxd) return 0;

代码也很简单了

#include<bits/stdc++.h>
using namespace std;
int goal[5][5]={{1,1,1,1,1},{0,1,1,1,1},{0,0,2,1,1},{0,0,0,0,1},{0,0,0,0,0}};
int Map[5][5],T,pd;
int fx[8]={1,-1,2,-2,1,-1,2,-2};
int fy[8]={2,2,-1,-1,-2,-2,1,1};
int stx,sty;
int h(){
	int ret=0;
	for(int i=0;i<5;i++)
		for(int j=0;j<5;j++)
			if(Map[i][j]!=goal[i][j]) ret++;
	return ret;
}
bool IDA(int maxd,int d,int x,int y){
	
	if(!h()) return 1;
	if(h()+d-1>maxd) return 0;
	for(int i=0;i<8;i++){
		int x1=x+fx[i];
		int y1=y+fy[i];
		if(x1<0||x1>4||y1<0||y1>4) continue;
		swap(Map[x1][y1],Map[x][y]);
		if(IDA(maxd,d+1,x1,y1)) return 1;
		swap(Map[x1][y1],Map[x][y]);
	}
	return 0;
} 
int main(){
	//freopen("1.in","r",stdin);
	cin>>T;
	while(T--){
		pd=0;
		for(int i=0;i<5;i++){
			char s[5];scanf("%s",s);
			for(int j=0;j<5;j++){
				if(s[j]-'0'==0) Map[i][j]=0;
				if(s[j]-'0'==1) Map[i][j]=1;
				if(s[j]=='*') Map[i][j]=2,stx=i,sty=j;
			}
		}
		for(int i=1;i<=15;i++)
			if(IDA(i,0,stx,sty)){pd=i;break;}
		if(pd) cout<<pd<<endl;
		else cout<<"-1"<<endl;
	}
}

 

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FSYo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值