📅  最后修改于: 2023-12-03 15:42:17.632000             🧑  作者: Mango
给定一个n*n的方格迷宫,每个方格表示一个房间。每个房间有4个门分别通向上下左右四个方向,其中某些门可以通往围墙之外。你可以从某个指定起点出发,尝试访问所有可达房间,然后返回起点。请写一个递归算法来找到此旅程的长度的最小值。
函数原型:int minCost(int points[][n])。
输入数据的格式如下:
第一行是整数n(2<=n<=18),表示迷宫的大小。
接下来的n行分别包含n个数字,表示房间的状态。状态为1表示该方格房间有通向上下左右四个方向的门,为0则表示无门、或该方向通向围墙。
最后一行是起点的坐标。
例如:
4
1 1 0 0
0 1 1 0
1 1 1 0
1 1 1 1
0 0
输出数据的格式应该如下:
第一行是最小成本。
第二行是遍历的路径。如果有多个,只需选择其中之一。
例如:
7
(0,0)->(0,1)->(1,1)->(1,2)->(2,2)->(2,1)->(3,1)->(3,2)->(2,0)->(1,0)->(0,0)
题目可以看作是求一个无向图的哈密顿回路,即从任意一个点出发,沿着图中的边走一次经过每个点且只经过一次,最终回到起点的路径。问题可以转换为寻找从起点出发,穿过所有点的最短路径。可以使用动态规划解决。
我们可以定义状态dp(i, S) 表示从起点0出发,走过集合S (即已经走过的点),最后到达点i的最小代价。其中,集合 S 内的点已经被遍历过,即已经经过“哈密顿回路”的部分路径了。集合 S 的元素全部为首尾两个点,即 S= {0, i}。因为最后需回到起点。
初始状态为dp(i,{0,i})=cost(0,i),其中,cost(x,y) 表示x和y之间的距离(本题中设距离为欧式距离)。
转移方程为:
若集合S中元素个数≥2,状态转移方程为
dp(i,S)=min{dp(j,S-{i})+cost(j,i)},其中j∈S,且j≠i
若集合S中仅有一个元素,有
dp(i,{0})=cost(0,i)
所以,状态转移方程为:
dp(i, S) = min(dp(j, S-{i}) + cost(j, i)), j∈S 且 j≠i
dp(i, {0}) = cost(0, i)
const int MAXN = 18;
int cost[MAXN][MAXN];
int dp[1 << MAXN][MAXN];
int num_points;
int minCost(int points[][MAXN])
{
num_points = 0;
for (int i = 0; i < MAXN * MAXN; i++)
{
int x = i / MAXN, y = i % MAXN;
if (points[x][y])
{
for (int dx = -1; dx <= 1; dx++)
{
for (int dy = -1; dy <= 1; dy++)
{
if (dx * dx + dy * dy == 1)
{
int nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < MAXN && ny >= 0 && ny < MAXN && points[nx][ny])
{
cost[i][nx * MAXN + ny] = cost[nx * MAXN + ny][i] = abs(x - nx) + abs(y - ny);
}
}
}
}
num_points++;
}
}
memset(dp, -1, sizeof(dp));
for (int i = 0; i < MAXN; i++)
{
dp[1<<i][i] = cost[0][i*MAXN];
}
for (int s = 0; s < (1<<num_points); s++)
{
for (int i = 0; i < num_points; i++)
{
if (!(s & (1<<i))) continue;
for (int j = 0; j < num_points; j++)
{
if (i == j || !(s & (1<<j))) continue;
int state = s & ~(1<<i);
if (dp[state][j] < 0) continue;
if (dp[s][i] < 0 || dp[s][i] > dp[state][j] + cost[j*MAXN+i])
{
dp[s][i] = dp[state][j] + cost[j*MAXN+i];
}
}
}
}
return dp[(1<<num_points)-1][0];
}