洛谷 P2534 [AHOI2012]铁盘整理(IDA*(dfs+迭代加深+估值函数))

题目链接:P2534 [AHOI2012]铁盘整理

题解:本篇为IDA*的应用,做这题前建议先做前一篇文章的题目:点这

这题中难在估值函数的确定,总的来说IDA*基本上格式都差不多,主要难在估值函数的确定上,估值函数确定的好就能很好的优化时间,我们可以发现,铁盘中的单调序列数即为我们要翻转的次数,比如12543我们需要翻转2次,恰好对应了 12,345两个单调序列,那么我们就可以用数组中单调队列是数量来代表H,即到达最终状态的操作次数,其他地方IDA*基本上都一样了

细节见代码注释:

#include<iostream>
#include<stack>
#include<list>
#include<set>
#include<vector>
#include<algorithm>
#include<math.h>
#include<numeric>
#include<map>
#include<cstring>
#include<queue>
#include<iomanip>
#include<cmath>
#include<queue>
#include <bitset>
#include<unordered_map>
	#ifndef local
	#define endl '\n'
#endif */
#define mkp make_pair
using namespace std;
using std::bitset;
typedef long long ll;
typedef long double ld;
const int inf=0x3f3f3f3f;
const ll MAXN=2e6+10;
const ll N=2e5+100;
const ll mod=1e9+7;
const ll hash_p1=1610612741;
const ll hash_p2=805306457;
const ll hash_p3=402653189;
//-----------------------------------------------------------------------------------------------------------------*/
// ll head[MAXN],net[MAXN],to[MAXN],edge[MAXN]/*流量*/,cost[MAXN]//费用;
/* 
void add(ll u,ll v,ll w,ll s){
	to[++cnt]=v;net[cnt]=head[u];edge[cnt]=w;cost[cnt]=s;head[u]=cnt;
	to[++cnt]=u;net[cnt]=head[v];edge[cnt]=0;cost[cnt]=-s;head[v]=cnt;
}
struct elemt{
	int p,v;
};
-----------------------------------
求[1,MAXN]组合式和逆元 
ll mi(ll a,ll b){
	ll res=1;
	while(b){
		if(b%2){
			res=res*a%mod;
		}	
		a=a*a%mod;
		b/=2;
	}
	return res;
}
ll fac[MAXN+10],inv[MAXN+10]
ll C(int m,int n){//组合式C(m,n); 
	if(!n){
		return 1;
	}
	return fac[m]*(inv[n]*inv[m-n]%mod)%mod;
}
fac[0]=1;inv[0]=1;
for(ll i=1;i<=MAXN;i++){
	fac[i]=(fac[i-1]*i)%mod;
	inv[i]=mi(fac[i],mod-2);
}
---------------------------------
 unordered_map<int,int>mp;
//优先队列默认小顶堆 , greater<int> --小顶堆  less<int> --大顶堆  
priority_queue<elemt,vector<elemt>,comp>q;
struct comp{
	public:
		bool operator()(elemt v1,elemt v2){
			return v1.v<v2.v;
		}
};
	set<int>::iterator it=st.begin();
*/
//emplace_back()  等于push_back(),但效率更高,传输pair时emplace_back(i,j)==push_back({i,j}) 
// vector<vector<int>>edge; 二维虚拟储存坐标 
//-----------------------------------------------------------------------------------------------------------------*/
  //map<int,bool>mp[N]; 
  //emplace_back() 
int dx[8]={1,1,2,2,-1,-1,-2,-2};
int dy[8]={2,-2,1,-1,2,-2,1,-1};
//目标状态
int key[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 c[10][10];
int h(){//到终点的估价函数
	int res=0;
	for(int i=0;i<5;i++){
		for(int j=0;j<5;j++){
			if(key[i][j]!=c[i][j]){
				res++;
			}
		}
	}
	if(res){//若当前存在res个不同点,则最少也需要res-1次交换
		res--;
	}
	return res;
}
int success=0;//标记是否有可行解
void A_STAR(int dep,int maxdep,int x,int y){//当前深度,允许运行的最大深度,空格子坐标
	if(dep==maxdep){//走到底了
		if(h()==0){//找到可行解了
			success=1;
		}
		return ;
	}
	for(int i=0;i<8&&!success;i++){//枚举转移(若要记录路径可以直接用个pre数组记录转移即可)
		int fx=x+dx[i],fy=y+dy[i];
		if(fx>=0&&fx<5&&fy>=0&&fy<5){
			swap(c[x][y],c[fx][fy]);//更新状态
			if(dep+1+h()<=maxdep){//继续往下转移可能为答案的情况
				A_STAR(dep+1,maxdep,fx,fy);
			}
			swap(c[x][y],c[fx][fy]);//回溯状态
		}
	}
}
int main(){
/*cout<<setiosflags(ios::fixed)<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(不含整数部分)*/
/*cout<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(含整数部分)*/
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);//同步流
	int t;
	cin>>t;
	while(t--){
		success=0;
		int sx,sy;
		for(int i=0;i<5;i++){
			for(int j=0;j<5;j++){
				char d;
				cin>>d;
				if(d=='0'){
					c[i][j]=0;
				}
				else if(d=='1'){
					c[i][j]=1;
				}
				else{
					c[i][j]=2;
					sx=i,sy=j;
				}
			}
		}
		for(int i=0;i<=15;i++){//精髓(降低复杂度)
			//我们可能会在一个没有解(或解很深的地方无限递归然而题目中要求输出任何的一组解),
			//所以我们限制一个深度,让它去遍历更多的分支,去更广泛地求解,(其实和BFSBFS有异曲同工之妙)。
			A_STAR(0,i,sx,sy);
			if(success){
				cout<<i<<endl;
				break;
			}
		}
		if(!success){
			cout<<-1<<endl;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值