题目链接:
http://poj.org/problem?id=2286
题意:
刚开始给出如图所示的一个井字形棋盘,每个棋盘上放有一个数,这个数一定是1,2,3中的一个,按图所示给列标号,现在有8种操作,每种操作分别为ABCDEFGH对应图中标号,每次操作相当于将这一列往标号的方向滚动一格,题上图示为先操作A,再操作C,问最少几次操作使得中心的8个格子数字相同,输出最后中心的数字,输出操作方式,不用操作则输出 No moves needed
题解:
IDA*搜索,这道题的估价比较直白,直接找中心八个数中的数量最多的数出现的次数 pri ,然后若当前步数 dep+8 - pri > ans 则不继续往下搜。
注意刚开始给棋盘标号,一定要细心,我就因为一个数字标错调了半个小时,QAQ。
还有一个很大的优化点,那就是按题上标号时,如果上一步操作了A,那么这步一定不要操作G,因为会抵消上步操作的作用,而刚开始我想着用sum[i]表示操作i的总次数,操作i一定不会超过7次,这样想着去优化,结果优化效果并不明显直接TLE。
代码:
#include
#include
#include
#include
using namespace std;
int tt,s[25],cnt[4],ans,path[40];
int check(int *w)
{
if (w[7]==w[8]&&w[8]==w[9]&&w[9]==w[13]&&w[13]==w[12])
if (w[12]==w[16]&&w[16]==w[17]&&w[17]==w[18])
{
tt=w[9];
return 1;
}
return 0;
}
//此处标号一定小心!!!!
int pan[8][7]={ {1, 3, 7,12,16,21,23}, //A
{2, 4, 9,13,18,22,24}, //B
{11,10,9, 8, 7, 6, 5}, //C
{20,19,18,17,16,15,14}, //D
{24,22,18,13, 9, 4, 2}, //E
{23,21,16,12, 7, 3, 1}, //F
{14,15,16,17,18,19,20}, //G
{5, 6, 7, 8, 9, 10,11}}; //H
int fan[8]={5,4,7,6,1,0,3,2};
int num[8]={7,7,7,7,7,7,7,7};
void rotate(int x,int *k)
{
int tmp=k[pan[x][0]];
for (int i=0;i<6;i++)
k[pan[x][i]]=k[pan[x][i+1]];
k[pan[x][6]]=tmp;
}
int dfs(int dep,int *now,int *mov,int pre)
{
if (check(now)) return 1;
cnt[1]=0;cnt[2]=0;cnt[3]=0;
cnt[now[7]]++;cnt[now[8]]++;cnt[now[9]]++;cnt[now[13]]++;
cnt[now[12]]++;cnt[now[16]]++;cnt[now[17]]++;cnt[now[18]]++;
int pri=max(cnt[3],max(cnt[1],cnt[2]));
if (dep+8-pri>ans)
return 0;
for (int i=0;i<8;i++)
if (fan[i]!=pre)
{
rotate(i,now);
mov[dep+1]=i;
if (dfs(dep+1,now,mov,i))return 1;
rotate(fan[i],now);
}
return 0;
}
int main()
{
while(scanf("%d",&s[1]))
{
if (s[1]==0) return 0;
for (int i=2;i<=24;i++)
scanf("%d",&s[i]);
if (check(s))
{
printf("No moves needed\n");
printf("%d\n",s[9]);
continue;
}
for (int i=1;i<=29;i++)
{
ans=i;
int hh[25];
for (int i=1;i<=24;i++) hh[i]=s[i];
if (dfs(0,hh,path,-1)) break;
}
for (int i=1;i<=ans;i++)
{
char c=path[i]+'A';
printf("%c",c);
}
printf("\n");
printf("%d\n",tt);
}
}