漫游小镇 解题报告

来源:互联网 发布:大闹天宫瑰羽进阶数据 编辑:程序博客网 时间:2024/06/09 20:01

漫游小镇

 一个正方形的镇区分为 N2 个小方块(1<= N <= 7)。农场位于方格的左上角,集市位于左下角。贝茜穿过小镇,从左上角走到左下角,刚好经过每个方格一次。当 N=3 时,贝茜的漫游路径可能如下图所示:


写一个程序,对于给出的 N 值,计算贝茜从农场走到集市有多少种唯一的路径。

PROGRAMNAME: betsy


INPUTFORMAT

行 1: 一个整数N (1 <= N <= 7)

 SAMPLEINPUT (file betsy.in)

3


OUTPUTFORMAT

只有一行。输出一个整数表示唯一路径的数量。

SAMPLEOUTPUT (file betsy.out)

这道题暴搜是过不了的,,不过考试的时候用了大概十分钟吧写出来,,然后让它跑完了打表。。。

虽然有点不怀好意,但是为我节省了不少时间呢。。

【代码】(不要理打表,,其实我的搜索是对的,,只不过时间有点长。。)

#include<iostream>#include<cstdio>#include<cstring>using namespace std;//int sx[4]={1,-1,0,0};//int sy[4]={0,0,1,-1};int n,ans;//int a[100][100];int b[100];/*void dfs(int s,int t,int dep){int r,x,y;if (s==n&&t==1){if (dep==n*n)  ans++;return;}for (r=0;r<4;++r){x=s+sx[r];y=t+sy[r];if (x>0&&y>0&&x<=n&&y<=n&&a[x][y]==0){a[x][y]=1;dfs(x,y,dep+1);a[x][y]=0;}}}*/int main(){freopen("betsy.in","r",stdin);freopen("betsy.out","w",stdout);scanf("%d",&n);//a[1][1]=1;//dfs(1,1,1);//printf("%d",ans);b[1]=1; b[2]=1; b[3]=2; b[4]=8; b[5]=86; b[6]=1770; b[7]=88418;printf("%d",b[n]);return 0;}

好吧来说说正解(摘自nocow题解):

搜索

这道题要使用DFS加上优化才可以过。朴素的搜索只能解决到N=5,6会超时。于是要加上一些优化。

优化1

不走死胡同!所谓死胡同,就是走进去以后就再也无法走出来的点。

一种简单的想法是:任意时刻,必须保证和当前所在位置不相邻的未经点至少有两个相邻的未经点。基于这种想法,可以采取这样的优化:

当前点周围的点D,如果只有一个与D相邻的未经点,则点D为必经点。

显然,如果当前点周围有两个或两个以上的符合上述条件的必经点,则无论走哪个必经点都会造成一个死胡同,需要剪枝。

如果当前点周围只有一个必经点,则一定要走到这个点。

如果该点周围没有必经点,则需要枚举周围每一个点。

该优化的力度很大,可以在0.2秒内解决N=6,但N=7仍然需要2秒左右的时间。

优化2

不要形成孤立的区域。如果行走过程中把路一分为二,那么肯定有一部分再也走不到了,需要剪枝。

形成孤立的区域的情况很多,如果使用Floodfill找连通快,代价会很大,反而会更慢。我只考虑了一种最容易出现特殊情况,即:

当前点左右都是已经点(包括边缘),而上下都是未经点;

当前点上下都是已经点(包括边缘),而左右都是未经点。

这样就会形成孤立的区域,只要将出现这种情况的状态都剪掉即可。

加上优化2,N=7也能在0.3s解决了。

【代码】(依然是找的标算,不过是pascal的,自己打了一遍,基本都看懂了,,加了点注释)

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int dx[5]={0,1,0,0,-1};int dy[5]={0,0,1,-1,0};bool map[9][9];int n,nn,i,sum;bool ok(int x,int y)//优化2,判断是否会形成孤立的区域 {int i,j;if (map[x+1][y]==true&&map[x-1][y]==true&&map[x][y+1]==false&&map[x][y-1]==false)  return false;if (map[x+1][y]==false&&map[x-1][y]==false&&map[x][y+1]==true&&map[x][y-1]==true)  return false;return true;}  int get(int x,int y)//优化1,return点(x,y)周围有几个未经点  { int c,i; c=0; for (i=1;i<=4;++i)   if (!map[x+dx[i]][y+dy[i]])     c++; return c; }void dfs(int x,int y,int step){int i,j,c,tx,ty;if (x==n&&y==1) {if (step==nn)   sum++;return;}if (step==nn) return;c=0;//优化1,开始寻找“必经点” for (i=1;i<=4;++i)  if (!map[x+dx[i]][y+dy[i]])  {  if (get(x+dx[i],y+dy[i])<=1)  {  c++;  tx=x+dx[i];  ty=y+dy[i];  if (tx==n&&ty==1) c--;  if (c>1) break;    }  }if (c>1) return;//优化1,剪枝 if (c==1)//优化1,如果只有一个必经点,则一定要走这个点 {map[tx][ty]=true;dfs(tx,ty,step+1);map[tx][ty]=false;return;} for (i=1;i<=4;++i)//优化1,否则枚举周围的每一个点   if (!map[x+dx[i]][y+dy[i]]&&ok(x+dx[i],y+dy[i]))  {  map[x+dx[i]][y+dy[i]]=true;  dfs(x+dx[i],y+dy[i],step+1);  map[x+dx[i]][y+dy[i]]=false;  }}int main(){scanf("%d",&n);for (i=0;i<=n+1;++i){map[i][0]=true;//将边界值都赋为true,有效地防止越界 map[i][n+1]=true;map[0][i]=true;map[n+1][i]=true;}nn=n*n;map[1][1]=true;dfs(1,1,1);printf("%d",sum);return 0;} 
【心得】

朴素的搜索+小聪明有时候是非常必要的,,但是厉害的优化一定要学。。考试时是不择手段的拿分,,但是过后要学一学正解,,不能保证以后这样的题会不会再遇到。。。

会做才是硬道理,骗分的最高境界就是不骗分。——《骗分导论》

0 0