连通块+记忆性递归的综合运用
这里x,y的设置反我平常的习惯,搞得我有点晕
实际上可以一输入就交换x,y的数据的
如果设置y1为全局变量的话会warning:
warning: built-in function 'y1' declared as non-function
所以我改成p和q了
刚开始判断能不能相连是靠连通块
后面求最短线段数是靠记忆性递归
代码如下:
#include
struct Min{
int x_d;//x轴的方向
int y_d;//y轴的方向
int len;
}min[77][77];
void fill(int color, int x, int y);
bool judge(int m1, int n1, int m2, int n2);
void dg(int y, int x, int color, int y_d, int x_d);
int map[77][77];
int w, h, p1, q1, p2, q2;
int main(void)
{
//板子输入
scanf("%d%d", &w, &h);
getchar();
for(int i = 1; i <= h; i++)
{
char c;
for(int j = 1; j <= w; j++)
if((c = getchar()) == 'X')
map[i][j] = 1;
else
map[i][j] = 0;
getchar();
}
//开始填充连通块
int color = 2;
fill(color++, 0, 0);
for(int i = 1; i <= h; i++)
for(int j = 1; j <= w; j++)
if(map[i][j] == 0)
fill(color++, i, j);
//开始判断并计算
int n;
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%d%d%d%d", &p1, &q1, &p2, &q2);
if(judge(p1, q1, p2, q2))
{
printf("impossible\n");
continue;
}
//重置min数组
for(int i = 0; i <= h + 1; i++)
for(int j = 0; j <= w + 1; j++)
min[i][j].len = 100, min[i][j].x_d = 0, min[i][j].y_d = 0;
min[q1][p1].len = 0;
//求最短线段数(从x1,y1到x2,y2)
if(map[q1 + 1][p1] != 1) dg(q1 + 1, p1, map[q1 + 1][p1], 1, 0);
if(map[q1 - 1][p1] != 1) dg(q1 - 1, p1, map[q1 - 1][p1], -1, 0);
if(map[q1][p1 + 1] != 1) dg(q1, p1 + 1, map[q1][p1 + 1], 0, 1);
if(map[q1][p1 - 1] != 1) dg(q1, p1 - 1, map[q1][p1 - 1], 0, -1);
//得到最短线段数
int minn = 100;
if(map[q2 + 1][p2] != 1)
{
int tmp = min[q2 + 1][p2].len + ((min[q2 + 1][p2].x_d == 0 && min[q2 + 1][p2].y_d == -1) ? 0 : 1);
minn = (minn < tmp) ? minn : tmp;
}
if(map[q2 - 1][p2] != 1)
{
int tmp = min[q2 - 1][p2].len + ((min[q2 - 1][p2].x_d == 0 && min[q2 - 1][p2].y_d == 1) ? 0 : 1);
minn = (minn < tmp) ? minn : tmp;
}
if(map[q2][p2 + 1] != 1)
{
int tmp = min[q2][p2 + 1].len + ((min[q2][p2 + 1].x_d == -1 && min[q2][p2 + 1].y_d == 0) ? 0 : 1);
minn = (minn < tmp) ? minn : tmp;
}
if(map[q2][p2 - 1] != 1)
{
int tmp = min[q2][p2 - 1].len + ((min[q2][p2 - 1].x_d == 1 && min[q2][p2 - 1].y_d == 0) ? 0 : 1);
minn = (minn < tmp) ? minn : tmp;
}
//输出
if(minn > 10) printf("impossible\n");
else printf("%d\n", minn);
}
return 0;
}
void fill(int color, int x, int y)
{
if(map[x][y]) return;
if(x < 0 || y < 0 || x > h + 1 || y > w + 1) return;
map[x][y] = color;
fill(color, x + 1, y), fill(color, x - 1, y);
fill(color, x, y + 1), fill(color, x, y - 1);
return;
}
bool judge(int m1, int n1, int m2, int n2)
{
int tmp1[4] = {map[n1 + 1][m1], map[n1 - 1][m1], map[n1][m1 + 1], map[n1][m1 - 1]};
int tmp2[4] = {map[n2 + 1][m2], map[n2 - 1][m2], map[n2][m2 + 1], map[n2][m2 - 1]};
for(int i = 0; i < 4; i++)
{
if(tmp1[i] == 1) continue;
for(int j = 0; j < 4; j++)
{
if(tmp2[j] == 1) continue;
if(tmp1[i] == tmp2[j]) return false;
}
}
return true;//只是代表是否执行if,而不是能不能连通
}
void dg(int y, int x, int color, int y_d, int x_d)
{
if(x < 0 || y < 0 || x > w + 1 || y > h + 1) return;
if(map[y][x] != color) return;
int tmp = min[y - y_d][x - x_d].len + ((x_d == min[y - y_d][x - x_d].x_d && y_d == min[y - y_d][x - x_d].y_d) ? 0 : 1);
if(tmp > min[y][x].len) return;//等于的话不用返回
min[y][x].len = tmp, min[y][x].x_d = x_d, min[y][x].y_d = y_d;
dg(y + 1, x, color, 1, 0);
dg(y - 1, x, color, -1, 0);
dg(y, x + 1, color, 0, 1);
dg(y, x - 1, color, 0, -1);
return;
}
这里实际上可以改一下目的地的color(本来是1),使旁边的块可以直接走到目的地,而不是目的地旁边
只要在4个方向的递归开始的时候改成相应的颜色就可以了,记得改回来,以后还要用
这样就不会显得累赘,可以把求最短线段数部分和得到最短线段数部分合并,代码会更短一点
懒得改了