题目:Golygons
题意:平面上有k个障碍点。 从(0,0)点出发,第一次走1个单位,第二次走2个单位,……,第n次走n个单位,恰好回到(0,0)。 要求只能沿着东南西北方向走,且每次必须转弯90°(不能沿着同一个方向继续走,也不能后退)。 走出的图形可以自交,但不能经过障碍点。输出所有可走的方向路径序列。
思路:标准dfs回溯。。。开始用pair和map解决负坐标问题,可老是TLE,最后参考了网上的把代码都向上平移了100个距离。
(1)输出:将所有障碍点加上100,超出地图范围的不要!
(2)枚举:从原点开始枚举4个方向,除了原点4个方向都可以走外,其他点都是根据上一个点的方向后,排除当前不能走的后剩下的继续递归。
(3)判断:枚举每个点时预先将上点坐标和本次的走的步数和方向进行查找此过程时候出现障碍物,出现则不走,否则走。
(4)剪枝:预先打步数表,sum[n]代表第n次最多走几步,如果当前坐标到原点的距离(|x|+|y|) 大于 最多还能走的步数(sum[20] - sum[k]) 即可剪枝!
参考:JeraKrs博客
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <cstring>
using namespace std;
const int maxn = 250;
const int expand = 100;
//typedef pair<int,int> coor;
//map<coor,int>g;
int g[maxn][maxn];
int n,path[maxn],ans;
char dir[] = {"ensw"};
int dx[] = {1,0,0,-1};
int dy[] = {0,1,-1,0};
int sum[21];
inline bool exceed(int x,int y){//超出地图范围
if(abs(x)+expand > maxn || abs(y)+expand > maxn) return true;
return false;
}
inline bool judge(int x,int y,int st,int d){//判断走的过程是否穿越障碍物
for(int i=0;i<st;i++){
x += dx[d]; y += dy[d];
if(exceed(x,y)) continue;
if(g[x+expand][y+expand] == -1) return false;
}
return true;
}
void dfs(int x,int y,int steps,int prev){
if(abs(x) + abs(y) > sum[20] - sum[steps]) return;//剪枝:当前位置到原点距离非常大直接退出
if(steps > n){
if(x == 0 && y == 0){
for(int i=1;i<steps;i++) printf("%c",dir[path[i]]);printf("\n");
ans++;
}
return;
}
//int prev = path[steps-1];
for(int i=0;i<4;i++){
if(i == prev || i+prev == 3) continue;//排除直走和后退
int tx = x + steps*dx[i] , ty = y + steps*dy[i];
if(judge(x,y,steps,i) && !g[expand+tx][expand+ty]){
path[steps] = i;//保存方向
g[expand+tx][expand+ty] = 1;
dfs(tx,ty,steps+1,i);
g[expand+tx][expand+ty] = 0;
}
}
}
int main()
{
int t,k,u,v;
for(int i=1;i<=20;i++) sum[i] = sum[i-1] + i;//将所有步数打表用于剪枝
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&k);
memset(g,0,sizeof(g));
for(int i=0;i<k;i++){
scanf("%d%d",&u,&v);
if(exceed(u,v)) continue;
g[u+expand][v+expand] = -1;
}
path[0] = -3;
ans = 0;
dfs(0,0,1,-3);
printf("Found %d golygon(s).\n\n",ans);
}
return 0;
}