其实这题可以不用状压。。
提供一种新思路。
我们在读入目标棋盘的时候,把当前位置的数和当前棋盘进行比较,如果不一样,如果当前是(1),目标是(0),那么我们就把当前位置加入(needmove)队列里去,否则加入(needgot)队列里去。
然后我们两遍循环,对这两个队列两两匹配,构成一个操作,表示把(needmove)里的一个位置上的(1)移到(needgot)里的一个位置上去,同时保存该操作需要的步数,就是这两个位置的曼哈顿距离。
然后进行(DFS),枚举所有操作,若这两个位置都没变成目标状态,那么标记一下,进行这个操作,然后回溯。
还可以加一个最优性剪枝,降低时间复杂度。
这样,总能得到最优的方案。
时间复杂度也比较优秀。
码量也很少。
#include <cstdio>
#define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define Close fclose(stdin);fclose(stdout);
int abs(int x){
return x > 0 ? x : -x;
}
struct Node{
int from, to, dis;
}e[4000];
int a[5][5], b[5][5];
int needmove[20], needgot[20], nm, ng, num, vism[4000], visg[4000], ans = 2147483647;
inline int read(){
char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
return ch - '0';
}
void dfs(int sum, int fi, int last){
if(sum > ans) return;
if(fi == nm){ ans = sum; return; }
for(int i = last; i <= num; ++i)
if(!vism[e[i].from] && !visg[e[i].to]){
vism[e[i].from] = 1;
visg[e[i].to] = 1;
dfs(sum + e[i].dis, fi + 1, i + 1);
vism[e[i].from] = 0; //回溯
visg[e[i].to] = 0;
}
}
int main(){
//Open("move");
for(int i = 1; i <= 4; ++i)
for(int j = 1; j <= 4; ++j)
a[i][j] = read();
for(int i = 1; i <= 4; ++i) //得到两个队列
for(int j = 1; j <= 4; ++j){
b[i][j] = read();
if(a[i][j] != b[i][j])
if(a[i][j] == 1)
needmove[++nm] = i * 10 + j;
else
needgot[++ng] = i * 10 + j;
}
for(int i = 1; i <= nm; ++i) //两两匹配记录所有可能的操作
for(int j = 1; j <= ng; ++j)
e[++num].from = needmove[i], e[num].to = needgot[j],
e[num].dis = abs(needmove[i] / 10 - needgot[j] / 10) + abs(needmove[i] % 10 - needgot[j] % 10);
dfs(0, 0, 1);
printf("%d
", ans);
//Close;
return 0;
}