题意:平面上有k个障碍点。从(0,0)出发,第一次走一个单位,第二次走2个单位,····第n次走n个单位,恰好回到(0,0)点。要求只能沿着东南西北方向走,且每次必须转弯90度(不能沿一个方向继续走,也不能后退)。走出的图形可以自交但不能经过障碍点,已经访问过的点不能再访问。输入n、k(1n20,0k50)和所有障碍点的坐标,所有满足条件的移动序列(用news,表示北、东、西、南),按照字典序从小到大输出,最后输出移动序列总数。
分析:本题是一道典型的回溯题,需剪枝,当当前的坐标到原点的x和y的距离和大于可以移动的步数,剪枝。按照四个方向搜索,每次搜索都判断是否经过障碍物。访问过的点不能再访问。
#include<cstdio>
#include<cstring>
#include<cctype>
#include<queue>
#include<iostream>
#include<vector>
#include<list>
#include<set>
using namespace std;
const int maxn = 300;
int originx = 150, originy = 150;//坐标原点
int G[maxn][maxn]; int n;
char route[maxn]; int rous;
int dy[] = { 0,1,-1,0 };
int dx[] = { 1,0,0,-1 };
char dir[] = { 'e','n','s','w' };
int vis[maxn][maxn];
void solve(int cur,int x,int y,int direction) {
if (cur == n + 1) {
if (x == originx && y == originy) {
for (int i = 1; i <= n; i++)cout << route[i];
cout << endl;
rous++;
}
return;
}
if (cur > n + 1)return;
for (int i = 0; i < 4; i++) {
if (i == direction||i==3-direction)continue;
int ddx = x + dx[i] * cur;
int ddy = y + dy[i] * cur;
if (vis[ddx][ddy])continue;
if((n - cur)*(n + cur + 1) / 2<(abs(ddx-originx)+abs(ddy-originy)))continue;
int ok = 0;
int a1, a2,a;
if (ddx == x) { a = x; a1 = y; a2 = ddy; }
else { a = y; a1 = x; a2 = ddx; }
if (a1 > a2) swap(a1, a2);
for (int j = a1; j <= a2; j++) {
if (ddx == x)if (G[a][j]) {
ok = 1; break;
}
if (ddy == y)if (G[j][ddy]) {
ok = 1; break;
}
}
if (!ok) {
route[cur] = dir[i];
vis[ddx][ddy] = 1;
solve(cur + 1, ddx, ddy, i);
vis[ddx][ddy] = 0;
}
}
}
int main() {
int kase;
cin >> kase;
while (kase-- > 0) {
memset(G, 0, sizeof(G));
memset(vis, 0, sizeof(vis));
rous = 0;
cin >> n;
int bar,barx,bary;
cin >> bar;
for (int i = 0; i < bar; i++) {
cin >> barx >> bary;
G[barx + originx][bary + originy] = 1;
}
solve(1, originx, originy, -1);
cout << "Found " << rous << " golygon(s)." << endl;
cout << endl;
}
//system("pause");
return 0;
}