AtCoder 224.D - 8 Puzzle on Graph
题意:
一个含有 9 个节点的图(边数 ≤ 36),有 8 个物品分布在 8 个节点上:第 i 分布在节点 vi 上。
问,能否通过空节点和其连节点间的物品移动,使得最终物品 i 在节点 i 上?
如果可,输出最小操作步数;否则,输出-1。
思路:
由 初始状态 变化为 最终状态,每次操作都会将状态改变。问最小操作次数。
和 奇怪的电梯 这题类似,都用到了bfs的重要性质,求最短路径(最小操作次数)。第一次搜到该点所走的路径是最短的!
所以可以用 字符串 和 map 来记录每一种状态。
每次找到空节点,用与其相连的节点来更新状态。同时记录操作次数。
第一次到达目标状态所用的操作次数便是最小操作次数。
Code:
const int N = 2010, mod = 1e9+7;
int T, n, m, a[N];
int e[N],ne[N],h[N],idx;
int f[N];
string s;
void add(int x,int y){
e[idx]=y,ne[idx]=h[x],h[x]=idx++;
}
void bfs()
{
queue<string> que;
que.push(s);
mp[s]=0;
while(que.size())
{
s=que.front();
que.pop();
int x;
for(int i=1;i<=9;i++) if(s[i]=='9') x=i; //找到空节点
for(int i=h[x];i!=-1;i=ne[i]) //和其相邻点交换
{
int tx=e[i];
string t=s;
swap(t[tx],t[x]);
if(mp.count(t)) continue; //之前到达过
mp[t]=mp[s]+1;
if(t==" 123456789") return;
que.push(t);
}
}
}
int main(){
Ios;
cin>>m;
mem(h,-1);
while(m--)
{
int x,y;
cin>>x>>y;
add(x,y);add(y,x);
}
for(int i=1;i<=8;i++)
{
int x;cin>>x;
f[x]=i;
}
for(int i=1;i<=9;i++){
if(!f[i]) f[i]=9;
s+=(char)(f[i]+'0');
}
s=" "+s;
if(s==" 123456789"){cout<<0;return 0;}
bfs();
int ans=mp[" 123456789"];
if(ans) cout<<ans;
else cout<<-1;
return 0;
}
很好的一道题!