AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=220,O=105; //O代表中心点(0,0)的坐标
const char dr[]="ensw"; //东北南西
const int dx[]={0,1,-1,0};
const int dy[]={1,0,0,-1};
int map[maxn][maxn],vis[maxn][maxn],t,n,k,tot;
char path[22]; //path是用来回溯的数组
int foot[22]; //n=1总共走1步,n=2总共走3步,n=3总共走6步...
void init1()
{
foot[0]=0;
for(int i=1;i<=20;i++)
{
foot[i]=foot[i-1]+i;
}
}
void init()
{
memset(map,0,sizeof(map));
memset(vis,0,sizeof(vis));
tot=0;
scanf("%d%d",&n,&k);
int x,y;
for(int i=0;i<k;i++)
{
scanf("%d%d",&y,&x);
map[O+x][O+y]=1; //输入障碍物
}
}
bool judge(int nx,int ny,int cur,int x,int y,int i)
{
if(abs(nx-O)+abs(ny-O)>foot[n]-foot[cur+1]) return false;
if(vis[nx][ny]||map[nx][ny]) return false; //该结点已被访问过或该结点上有障碍
if(nx==O&&ny==O&&cur!=n-1) return false; //不能恰好走第n次经过原点(在第n次前已经经过原点)
int tx=nx,ty=ny;
//走出的直线不能经过障碍物
while(tx!=x)
{
tx-=dx[i];
if(map[tx][y]) return false;
}
while(ty!=y)
{
ty-=dy[i];
if(map[x][ty]) return false;
}
return true;
}
void dfs(int cur,int x,int y,int d) //x,y为当前出发点的坐标,cur为回溯法的位置,d为当前的方向
{
if(cur==n&&x==O&&y==O)
{
tot++;
for(int i=0;i<n;i++)
{
printf("%c",path[i]);
}
printf("\n");
return;
}
if(cur==n) return; //走了n次还没有走到原点,直接回溯
for(int i=0;i<4;i++)
{
if(i==d||i+d==3) continue; //不能沿着同一方向或反方向走
int nx=x+(cur+1)*dx[i],ny=y+(cur+1)*dy[i];
if(judge(nx,ny,cur,x,y,i))
{
vis[nx][ny]=true;
path[cur]=dr[i];
dfs(cur+1,nx,ny,i);
vis[nx][ny]=false;
}
}
}
void solve()
{
dfs(0,O,O,-1);
printf("Found %d golygon(s).\n\n",tot);
}
int main()
{
init1();
scanf("%d",&t);
while(t--)
{
init();
solve();
}
return 0;
}
反思:在回溯法中,找到剪枝的条件很重要,并且回溯的过程可以用过形参记录一个结点的状态。