群里的小伙伴发了一道在题目,判断一个棋盘上连连看的两个点是否可以消除。
原题目的点参考:
http://acm.hdu.edu.cn/showproblem.php?pid=1175
思路:参考了这篇blog
http://blog.youkuaiyun.com/artzok/article/details/51622782
大概意思,两个点最多通过两次折点相连分两种情况:
1)两点在一条直线上,那么两点可以直接连通。判断是否是这种情况
2)如果两点不在一直线上,从其中一个点出发往4个方向引点,只要引出的任意一点只经过一次拐点可以到达两一个点,则两个点(最大这点两次)可以相连。
代码实现如下:
#include <iostream>
#define N (1024)
int map[N][N] = { 0 };
/**
* test that whether point 1 and point 2 is connected without corner.
*/
bool ZeroCornerLink(int array[][N], int x1, int y1, int x2, int y2)
{
// p1 and p2 can't connected without corner.
if (x1 != x2 && y1 != y2)
return false;
int offsetX = (x2 > x1) ? 1 : ((x2 < x1 ? -1 : 0));
int offsetY = (y2 > y1) ? 1 : ((y2 < y1 ? -1 : 0));
for (int y = y1 + offsetY, x = x1 + offsetX; x != x2 || y != y2;
x += offsetX, y += offsetY)
{
if (array[x][y] != 0)
return false;
}
return true;
}
/**
* test that whether point 1 and point 2 is connected with only one corner.
*/
bool OneCornerLink(int arry[][N], int x1, int y1, int x2, int y2)
{
// p1 and p2 can't connected without corner.
if (x1 == x2 || y1 == y2)
return false;
int offsetX = (x2 > x1) ? 1 : ((x2 < x1 ? -1 : 0));
int offsetY = (y2 > y1) ? 1 : ((y2 < y1 ? -1 : 0));
// scan from X offset at first.
bool direction = true; // true to indicate to scan from X first.
for (int i = 0; i < 2; i++)
{
int x = x1, y = y1;
direction = (i == 0) ? true : false;
while (x != x2 || y != y2)
{
if (arry[x][y] != 0)
break; // we have meet a block.
if (direction && x == x2)
direction = false; // change direction
if (!direction && y == y2)
direction = true; // change direction
if (direction)
x += offsetX;
else
y += offsetY;
}
if (x == x2 && y == y2)
return true; // it is connected.
}
return false;
}
int main(int argc, char **argv)
{
int mapRows, mapCols, nQueries;
while (scanf("%d%d", &mapRows, &mapCols) && mapRows)
{
for (int i = 1; i <= mapRows; i++)
{
for (int j = 1; j <= mapCols; j++)
scanf("%d", &map[i][j]);
}
scanf("%d", &nQueries);
while (nQueries--)
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
if (map[x1][y1] != map[x2][y2] || map[x1][y1] == 0 || x1 == x2&&y1 == y2)
{
printf("NO\n");
continue;
}
if (ZeroCornerLink(map, x1, y1, x2, y2))
{
printf("Yes\n");
continue;
}
else // corner link.
{
bool bConnected = false;
// scan from up-down
for (int x = x1 + 1, y = y1; !bConnected && x <= mapRows; ++x)
{
if (map[x][y] != 0) // block this direction
break;
if (OneCornerLink(map, x, y, x2, y2))
bConnected = true;
}
// scan from down-up
for (int x = x1 - 1, y = y1; !bConnected && x > 0; --x)
{
if (map[x][y] != 0) // block this direction
break;
if (OneCornerLink(map, x, y, x2, y2))
bConnected = true;
}
// scan from left-right
for (int x = x1, y = y1 + 1; !bConnected && y <= mapCols; ++y)
{
if (map[x][y] != 0) // block this direction
break;
if (OneCornerLink(map, x, y, x2, y2))
bConnected = true;
}
// scan from right-left
for (int x = x1, y = y1 - 1; !bConnected && y > 0; --y)
{
if (map[x][y] != 0) // block this direction
break;
if (OneCornerLink(map, x, y, x2, y2))
bConnected = true;
}
if (bConnected)
printf("Yes\n");
else
printf("No\n");
}
}
}
return 0;
}
复杂度分析:
假设输入的数组是 M x N
则一次扫描,外层4个循环最多扫描的次数为 M+N
ZeroCornerLink复杂度是 O(Max(M, N)),
OneCornerLink扫描次数为最多两个方向2 *(M+N)
因此最坏情况下的复杂度为 O((M+N)^2)
内循环应该有重复扫描的路线,但是并未带来数量级的复杂度提升。