題目:
連連看
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 27462 Accepted Submission(s): 6809
玩家鼠標先後點擊兩塊棋子,試圖將他們消去,然後遊戲的後臺判斷這兩個方格能不能消去。現在你的任務就是寫這個後臺程序。
注意:詢問之間無先後關係,都是針對當前狀態的!
題解:這題坑點好多,一開始的時候給了10s,說明搜索的時候應該情況很多的,因爲這個並不是找一個最優化的解,而是有轉彎次數的限制,這個體現在dfs裏面就不太好判斷,而且剪枝也是這道題的一個很難的地方。首先第一層剪枝,是在get到查詢之後的,如果兩個地方的數字不一樣,也就是連連看的圖案不一樣,那麼不需要搜索直接輸出NO,相應的情況還有該點的數值爲0的情況,也是不需要搜索。其次是在dfs時的剪枝,當轉彎次數超過兩次時,直接判斷當前方向上是否可達目標點,這個剪枝能把運行時間從8k降低到200ms。由於這次搜索的主要key是方向,那麼相應的修改一下模板,沒有用for循環來模擬方向,而是純粹手打,而且對於每種情況優先考慮和它同方向的搜索,不過這個優化的時間不怎麼明顯,大概只有20ms左右。代碼1是隻做了第一層優化的代碼,直接T,運氣好c++可以過,蛤蛤。
代碼:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
int map[1010][1010];
int dx[] = { 0,0,1,-1 };
int dy[] = { 1,-1,0,0 };
int n, m,ans,x2,y2;
void dfs(int x1, int y1, int dir, int times)
{
if (x1 == x2 && y1 == y2 && times <= 2)
{
ans = 1;
return;
}
if (times > 2)
return ;
map[x1][y1] = -1;
for (int i = 0; i < 4; i++)
{
int curx = x1 + dx[i];
int cury = y1 + dy[i];
if (curx > 0 && curx <= n && cury > 0 && cury <= m && map[curx][cury] == 0)
{
if (i == dir)
dfs(curx, cury, dir, times);
else
dfs(curx, cury, i, times + 1);
}
}
map[x1][y1] = 0;
return;
}
int main()
{
//freopen("input.txt", "r", stdin);
int q, x1, y1;
while (scanf("%d%d", &n, &m), !(m == 0 && n == 0))
{
//init();
for (int i = 0; i < n; i++)
{
for (int j = 0; j < m; j++)
{
scanf("%d", &map[i+1][j+1]);
}
}
scanf("%d", &q);
for (int i = 0; i < q; i++)
{
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
if (map[x1][y1] == map[x2][y2] && map[x1][y1] !=0)
{
map[x2][y2] = 0;
int temp = map[x1][y1];
ans = 0;
dfs(x1, y1, -1, -1);
if (ans)
printf("YES\n");
else
printf("NO\n");
map[x1][y1] = map[x2][y2] = temp;
}
else
puts("NO");
}
}
return 0;
}
代碼2:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1005;
int map[maxn][maxn], vis[maxn][maxn];
int n, m, x2, y2;
int ans;
//1,2,3,4stands for up,down,left,right
void dfs(int x, int y, int dir, int turn)
{
if (ans)
return;
if (x <= 0 || x > n || y <= 0 || y > m) //out of map
return;
if (turn >= 3) //turn out of limit
return;
if (x == x2 && y == y2) //get the goal
{
ans = 1;
return;
}
if (turn == 2) //剪枝,轉彎兩次時,看目的點的座標與現在的朝向是否在一個方向上,不在的話就返回
{
if (!((dir == 1 && y < y2 && x == x2) || (dir == 2 && y > y2 && x == x2) || (dir == 3 && x > x2 && y == y2) || (dir == 4 && x < x2 && y == y2)))
return;
}
if (map[x][y] != 0) //barrier
return;
if (vis[x][y]) //visited
return;
vis[x][y] = 1;
if (dir == 1)
{
dfs(x, y + 1, 1, turn);
dfs(x, y - 1, 2, turn + 1);
dfs(x - 1, y, 3, turn + 1);
dfs(x + 1, y, 4, turn + 1);
}
else if (dir == 2)
{
dfs(x, y - 1, 2, turn);
dfs(x, y + 1, 1, turn + 1);
dfs(x - 1, y, 3, turn + 1);
dfs(x + 1, y, 4, turn + 1);
}
else if (dir == 3)
{
dfs(x - 1, y, 3, turn);
dfs(x, y + 1, 1, turn + 1);
dfs(x, y - 1, 2, turn + 1);
dfs(x + 1, y, 4, turn + 1);
}
else if (dir == 4)
{
dfs(x + 1, y, 4, turn);
dfs(x, y + 1, 1, turn + 1);
dfs(x, y - 1, 2, turn + 1);
dfs(x - 1, y, 3, turn + 1);
}
vis[x][y] = 0;
return;
}
int main()
{
//freopen("input.txt", "r", stdin);
int q, x1, y1;
while (scanf("%d%d", &n, &m) != EOF, n || m)
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
scanf("%d", &map[i][j]);
}
}
scanf("%d", &q);
while (q--)
{
memset(vis, 0, sizeof(vis));
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
ans = 0;
if (x1 == x2 && y1 == y2)
puts("NO");
else if (map[x1][y1] == map[x2][y2] && map[x1][y1])
{
dfs(x1, y1 + 1, 1, 0);
dfs(x1, y1 - 1, 2, 0);
dfs(x1 - 1, y1, 3, 0);
dfs(x1 + 1, y1, 4, 0);
if (ans)
puts("YES");
else
puts("NO");
}
else
puts("NO");
}
}
return 0;
}