您的位置:首页 > 其它

漫游小镇 解题报告

2015-08-14 10:20 260 查看
漫游小镇

 一个正方形的镇区分为 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
);
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;
} 【心得】
朴素的搜索+小聪明有时候是非常必要的,,但是厉害的优化一定要学。。考试时是不择手段的拿分,,但是过后要学一学正解,,不能保证以后这样的题会不会再遇到。。。

会做才是硬道理,骗分的最高境界就是不骗分。——《骗分导论》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: