漫游小镇 解题报告
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)
2
这道题暴搜是过不了的,,不过考试的时候用了大概十分钟吧写出来,,然后让它跑完了打表。。。
虽然有点不怀好意,但是为我节省了不少时间呢。。
【代码】(不要理打表,,其实我的搜索是对的,,只不过时间有点长。。)
#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;
} 【心得】
朴素的搜索+小聪明有时候是非常必要的,,但是厉害的优化一定要学。。考试时是不择手段的拿分,,但是过后要学一学正解,,不能保证以后这样的题会不会再遇到。。。
会做才是硬道理,骗分的最高境界就是不骗分。——《骗分导论》
一个正方形的镇区分为 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)
2
这道题暴搜是过不了的,,不过考试的时候用了大概十分钟吧写出来,,然后让它跑完了打表。。。
虽然有点不怀好意,但是为我节省了不少时间呢。。
【代码】(不要理打表,,其实我的搜索是对的,,只不过时间有点长。。)
#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;
} 【心得】
朴素的搜索+小聪明有时候是非常必要的,,但是厉害的优化一定要学。。考试时是不择手段的拿分,,但是过后要学一学正解,,不能保证以后这样的题会不会再遇到。。。
会做才是硬道理,骗分的最高境界就是不骗分。——《骗分导论》
相关文章推荐
- linnet实例
- java实现文件的上传和下载
- IBM和JAMF的关系
- 小米:产品上的进步和战略上的退步
- 海水淡化膜:陶氏膜法海水淡化技术的应用与进展
- 13家中国域名商(国际域名)解析量报告(8月11日)
- IBM和JAMF的关系
- HDU 1879 继续畅通工程
- DWR与AJAX的微妙关系
- Android L——RecyclerView,CardView的导入和使用
- HDU 2846 Repository(字典树变形题)
- hdu 3308 LCIS
- WPF入门(四):简单绑定 - 静态资源绑定
- WPF入门(五):简单绑定 - 用户输入合法性验证
- WPF入门(六)样式Style
- WPF入门(七)绑定一个集合
- WPF入门(八)布局(layout)
- WPF入门(八)布局(layout) port 2
- android开发(1) - 登录界面的布局演示
- android开发(1) - 登录界面的布局演示