题目链接: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;
}