题意:有八个方向可以让数列移动,求最小移动次数使中间八个数值相同(数列移动规则看图·)
分析:两种方法:1.状态压缩(小白上有介绍)。观察可知终止状态分三种情况,中间八个要么为1,要么为2,或者为3,则先预处理,中间为1时,四周16个都不为一,中间为2时,16个不为2,
中间为3时,16个不为3,排列组合可知状态有C(8,24)种,状态有大约70多w,还是可求的,但无论我怎么写,在poj可以过,4.594s,在uva果断超时,有知道怎么实现的同学告知在下(uva上要求3s,poj上15s)
2。迭代加深。从小枚举深度,加剪掉 现有步数加预测最快到达步数还不能到达的
先粘状态压缩的代码:(4.594s)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<cmath>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
int forturn[8][7]={
{0,2,6,11,15,20,22},
{1,3,8,12,17,21,23},
{10,9,8,7,6,5,4},
{19,18,17,16,15,14,13},
{23,21,17,12,8,3,1},
{22,20,15,11,6,2,0},
{13,14,15,16,17,18,19},
{4,5,6,7,8,9,10}
};
int in[25],st[3],goal,symans;char pans[50];
bool done[1<<26];
struct node
{
int x,w,id;char p[50];
node(int xx=0,int ww=0,int i=0,char* pp="")
{
x=xx;w=ww,strcpy(p,pp);id=i;
}
}qx[1200000];
int solve(int x,int c)
{
int i=0,xx=x;
for(i=0;i<6;i++)
{
if(1&(x>>forturn[c][i]))xx^=(1<<forturn[c][i]);
xx=xx|(((x>>forturn[c][i+1])&1)<<forturn[c][i]);
}
if(1&(x>>forturn[c][6]))xx^=(1<<forturn[c][6]);
xx=xx|(((x>>forturn[c][0])&1)<<forturn[c][6]);
return xx;
}
void init()
{
int i,j;st[0]=st[1]=st[2]=0;
for(j=1;j<=3;j++)
for(i=23;i>=0;i--)
st[j-1]=(st[j-1]<<1)|(in[i]==j);
}
int bfs()
{
int i,j,last=INF;
int _start=0,_end=0;
memset(done,0,sizeof done);
for(i=0;i<3;i++)
{
qx[_end]=node(st[i],0,i,"");_end=(_end+1)%1200000;
done[st[i]+i*(1<<24)]=1;
}
while(_start!=_end)
{
node e=qx[_start];_start=(_start+1)%1200000;
if(e.w==last)return 1;
for(i=0;i<8;i++)
{
int cur=solve(e.x,i);
if(done[cur+e.id*(1<<24)])continue;
char ssa[50];
strcpy(ssa,e.p);
j=strlen(e.p);ssa[j]='A'+i;if(j<29)ssa[j+1]='\0';
if(cur==goal&&last==INF){last=e.w+1;strcpy(pans,ssa);symans=e.id;}
if(cur==goal&&strcmp(ssa,pans)<0){strcpy(pans,ssa);symans=e.id;}
qx[_end]=node(cur,e.w+1,e.id,ssa);_end=(_end+1)%1200000;
done[cur+e.id*(1<<24)]=1;
}
}
return -1;
}
int main()
{
int i;
//("code.txt","r",stdin);
while(~scanf("%d",&in[0]))
{
if(!in[0])break;
for(i=1;i<24;i++)
scanf("%d",&in[i]);
goal=235968;
init();
if(st[0]==goal||st[1]==goal||st[2]==goal)
{
printf("No moves needed\n%d\n",in[6]);
continue;
}
bfs();
printf("%s\n%d\n",pans,symans+1);
}
return 0;
}
迭代加深代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int in[24];
int turn[8][7]={0,2,6,11,15,20,22,
1,3,8,12,17,21,23,
10,9,8,7,6,5,4,
19,18,17,16,15,14,13,
23,21,17,12,8,3,1,
22,20,15,11,6,2,0,
13,14,15,16,17,18,19,
4,5,6,7,8,9,10};
int aim[8]={6,7,8,11,12,15,16,17};
int opp[8]={5,4,7,6,1,0,3,2};
int ans[50],D;
int togoal()
{
int i,cnt[3]={0};
for(i=0;i<8;i++)
cnt[in[aim[i]]-1]++;
return 8-max(cnt[0],max(cnt[1],cnt[2]));
}
void forturn(int cur)
{
int tmp=in[turn[cur][0]];
for(int i=0;i<6;i++)
in[turn[cur][i]]=in[turn[cur][i+1]];
in[turn[cur][6]]=tmp;
}
int dfs(int place)
{
if(place==D+1)return 0;
for(int i=0;i<8;i++)
{
forturn(i);
ans[place]=i;
int tmp;
if((tmp=togoal())==0)return 1;
if(place+tmp<=D)
{
if(dfs(place+1))return 1;
}
forturn(opp[i]);
}
return 0;
}
int main()
{
int i;
//freopen("code.txt","r",stdin);
while(~scanf("%d",&in[0]))
{
if(!in[0])break;
for(i=1;i<24;i++)
scanf("%d",&in[i]);
if(togoal()==0)
{
printf("No moves needed\n%d\n",in[aim[0]]);continue;
}
for(D=1;!dfs(1);D++)
;
for(i=1;i<=D;i++)
printf("%c",ans[i]+'A');
printf("\n%d\n",in[aim[0]]);
}
return 0;
}