关于马拦过河卒题解

 

关于马拦过河卒题解

焦祺 08-11-17

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)

Problem Description

棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上C点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为马拦过河卒
棋盘用坐标表示,A(0, 0)B(n, m)(n, m为不超过15的整数),同样马的位置坐标是需要给出的。现在要求你计算出卒从A点能够到达B点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。

Input

包括若干组数据。第一行一个数n表示数据的组数。
紧接着有n行,一行四个数据,分别表示B点坐标和马的坐标。

Output

对应每组输入数据,输出一个数据,表示所有的路径条数。

Sample Input

1

6 6 3 3

Sample Output

6

 

这题目看似是搜索题,于是我就按搜索题的一般方法入手

先用广搜,把没测试数据通过之后提交发现超时!

于是我对其进行进一步分析,发现有一两处可做稍微减枝的地方

减完之后再交再次TLE

几次的修改未果,最后一次还出现了MLE

即然出现了MTL,于是我便换成了DFS搜索的方法。

提交后发现还好,0KBMemery,不过还是TLE了!

几次的修改依然未果,无耐又转战为双向广搜,但这题要求的计算出方法总数,基本上要遍历出所有情况,所以启发式搜索一般也不能很好的发挥其优势。

后来在队友的提醒下,想起了递归算法有保存中间值的方法。

之前还做过类似的总结,可惜太久没有用遗忘了,

所以不时的看下自己的总结还是很有必要的。

以下是详细的解题过程,并从中得出更好的递推算法解题!

广度优先搜索:(解题代码)TLE

#include <iostream>

#include<queue>

using namespace std;

struct      Point

{

       int x;

       int y;

};

Point       start,end,house,N,P;

void bfs();

int           counter;

int           dir[][2]={{1,0},{0,1}};

int           housedir[][2] = {{1,2},{2,1},{-1,2},{2,-1},{-2,1},{1,-2},{-1,-2},{-2,-1}};

char g[16][16];

int main()

{

       int i,j,t,tempx,tempy,flag;

       start.x = start.y = 0;

       scanf("%d",&t);

       while (t--)

       {

              scanf("%d %d %d %d",&end.x,&end.y,&house.x,&house.y);

              //-----------开始画图:

              for (i=0;i<=end.x;i++)

                     for (j=0;j<=end.y;j++)

                            g[i][j] = '0';

              g[house.x][house.y] = '1';

              flag = 0;

              for (i=0;i<8;i++)

              {

                     tempx = house.x+housedir[i][0];

                     tempy = house.y+housedir[i][1];

                     if (tempx>=0 && tempx<=end.x && tempy>=0 && tempy<=end.y)

                            g[tempx][tempy] = '1';   //1为不可走

                     if ( (tempx == end.x && tempy == end.y))

                     {

                            flag == 1;

                            break;

                     }

              }

              if (flag == 1)

              {

                     printf("0/n");

                     continue;

              }

              g[end.x][end.y] = '2';

              counter = 0;

              bfs();

              printf("%d/n",counter);

       }

       return 0;

}

void bfs()

{

       queue<Point> Q; //建立一个队列

       N.x = start.x;  N.y = start.y;

       Q.push(N);

       while( !Q.empty() )

       {    

              N=Q.front();   //找队列中第一个数

              Q.pop();         //弹出来

              int i;

              //----------------------开始搜:

              for(i=0;i<2;i++)

              {

                     int tx=N.x+dir[i][0];

                     int ty=N.y+dir[i][1];

                     if(tx>=0 && tx<=end.x && ty>=0 && ty<=end.y)

                     {

                            if(g[tx][ty] != '1')                        //如果有路

                            {                                                             //将四个方向各点推入队列.

                                   P.x=tx;

                                   P.y=ty;

                                   Q.push(P);

//                                 printf("%d %d/n",tx,ty);

                            }

                            if (g[tx][ty] == '2')

                            {

                                   counter++;    

                            }

                     }

              }

       }

}

 

一般深度优先搜索:(解题代码)TLE,或有少许BUG

 

#include <iostream>

using namespace std;

 

struct      Point

{

       int x;

       int y;

};

 

Point       start,end,house,N,P;

int           housedir[][2] = {{1,2},{2,1},{-1,2},{2,-1},{-2,1},{1,-2},{-1,-2},{-2,-1}};

int           dir[][2]={{1,0},{0,1}};

char g[16][16];

int           counter;

void dfs(int x,int y);

 

int main()

{

       int i,j,t,tempx,tempy,flag;

       start.x = start.y = 0;

       scanf("%d",&t);

       while (t--)

       {

              scanf("%d %d %d %d",&end.x,&end.y,&house.x,&house.y);

              //-----------开始画图:

              memset(g,NULL,sizeof(g));

              g[house.x][house.y] = '1';

              flag = 0;

              for (i=0;i<8;i++)

              {

                     tempx = house.x+housedir[i][0];

                     tempy = house.y+housedir[i][1];

                     if (tempx>=0 && tempx<=end.x && tempy>=0 && tempy<=end.y)

                     {

                            g[tempx][tempy] = '1';   //1为不可走

                     }

                     if ( (tempx == end.x && tempy == end.y))

                     {

                            flag == 1;

                            break;

                     }

              }

              if (flag == 1)

              {

                     printf("0/n");

                     continue;

              }

              g[end.x][end.y] = '2';

              counter = 0;

              dfs(0,0);

              printf("%d/n",counter);

       }

       return 0;

}

 

void dfs(int x,int y)

{

       int i,tx,ty;

       if (g[x][y] == '2')

       {

              counter++;

              return;

       }

       for (i=0;i<2;i++)

       {

              tx = x + dir[i][0];

              ty = y + dir[i][1];

              if (tx>=0 && tx<=end.x && ty>=0 && ty<=end.y)

              {

                     if (g[tx][ty] != '1')

                     {

                            dfs(tx,ty);

                     }

              }

       }

      

}


深度优先搜索(保存路径) AC

 

用深度优先搜索(保存路径)提交了有十几次之多,终于搞定

用了几个小时的时间,差点没有崩溃!!

从原来的TLEMLE到最后的0MS0KB,还是很有成就感的。

很早之前就知道有递归保存路径防TLE的方法

但很久没有用还把它给忘记了,现在才知道它的强大。

不过这道题用这种法方需要考虑的地方很多!

 

比较经典的地方在

路径保存的时候每个节点要返回前面路径节点的和

即:a[i,j] = a[i-1,j] + a[i,j-1]

这样就可以在中间段时得到结果,不必重复走走过的路段。

 

AC代码:

#include <iostream>

using namespace std;

 

struct      Point

{

       int x;

       int y;

};

 

Point       start,end,house,N,P;

int           housedir[][2] = {{1,2},{2,1},{-1,2},{2,-1},{-2,1},{1,-2},{-1,-2},{-2,-1}};

int           dir[][2]={{1,0},{0,1}};

char g[17][17];

int           e[17][17];

 

int           counter;

int    dfs(int x,int y);

 

int main()

{

       int i,j,t,tempx,tempy,flag;

       start.x = start.y = 0;

       scanf("%d",&t);

       while (t--)

       {

              scanf("%d %d %d %d",&end.x,&end.y,&house.x,&house.y);

              //-----------开始画图:

              counter = 0;

              for (i=0;i<=end.x+1;i++)

              {

                     for (j=0;j<=end.y+1;j++)

                     {

                            e[i][j] = 0;

                            g[i][j] = '0';

                     }

              }

              g[house.x][house.y] = '1';

              for (i=0;i<8;i++)

              {

                     tempx = house.x+housedir[i][0];

                     tempy = house.y+housedir[i][1];

                     if (tempx>=0 && tempx<=end.x && tempy>=0 && tempy<=end.y)

                     {

                            g[tempx][tempy] = '1';   //1为不可走

                     }

              }

              if (g[end.x][end.y] == '1')

              {

                     printf("0/n");

                     continue;

              }

              g[end.x][end.y] = '2';

              counter = 0;

              dfs(0,0);

              printf("%d/n",counter);

       }

       return 0;

}

 

int dfs(int x,int y)

{

       int i,tx,ty;

       int b[2];

       if (g[x][y] == '2')

       {

              counter++;

              e[x][y] = 1;

              return e[x][y];

       }

       for (i=0;i<2;i++)

       {

              tx = x + dir[i][0];

              ty = y + dir[i][1];

              if (tx>=0 && tx<=end.x && ty>=0 && ty<=end.y)

              {

                     if (g[tx][ty] != '1')

                     {

                            if (e[tx][ty])

                            {

                                   counter+=e[tx][ty];

                                   //return e[tx][ty];

                            }

                            else

                            {

                                   e[tx][ty] = dfs(tx,ty);

                                  

                                   //return e[tx][ty];

                            }

                     }

              }

       //     b[i] = e[tx][ty];

       }

       if (e[x+1][y] || e[x][y+1])

       {

              return e[x+1][y]+e[x][y+1];

       }

       return 0;

}

 

 

递推算法(最高效):(AC

由以上的递归算法的实现上我更清楚地看到的题目的本质

从中可以看到其递推的公式,这样可以将算法效率提高至线性!

 

/

0

1

2

3

4

5

6

 

 

 

 

 

0

1

1

1

1

1

1

1

 

1

1

1

1

1

1

2

h

1

h

1

2

 

1

2

3

4

2

1

h

0

1

1

h

2

 

1

3

6

10

3

1

1

1

h

1

1

3

 

1

4

10

20

4

1

h

1

1

2

h

3

 

 

|

 

5

1

1

h

1

h

0

3

 

或可得到某种数列?!

6

1

2

2

3

3

3

6

 

递推公式:

类杨辉三角

a[i][j]= a[i-1][j]+a[i][j-1];

 

 

 

 

 

a[i,j]表示达到i,j的方案数。

递推if(ij)可达则a[i,j]=a[i-1,j]+a[i,j-1]

但要注意边界!

 

AC代码:

#include <iostream>

using namespace std;

struct      Point

{

       int x;

       int y;

};

Point       start,end,house;

int           a[17][17];

int           housedir[][2] = {{1,2},{2,1},{-1,2},{2,-1},{-2,1},{1,-2},{-1,-2},{-2,-1}};

int main()

{

       int i,j,k,t,n,m,tempx,tempy,countx,county;

       scanf("%d",&t);

       while(t--)

       {

              memset(a,0,sizeof(a));

              scanf("%d %d %d %d",&end.x,&end.y,&house.x,&house.y);

              a[house.x][house.y] = -1;

              for (i=0;i<8;i++)

              {

                     tempx = house.x+housedir[i][0];

                     tempy = house.y+housedir[i][1];

                     if (tempx>=0 && tempx<=end.x && tempy>=0 && tempy<=end.y)

                     {

                            a[tempx][tempy] = -1;   //不可达,方案数标记为-1

                     }

              }

              //注意边界!!

              a[0][0] = 1;

              for (i=0;i<=end.x;i++)

              {

                     for (j=0;j<=end.y;j++)

                     {

                            if (i == 0 && j == 0)     //首元素处理

                                   continue;

                            if (a[i][j] == -1)             //不可达标记处理

                                   continue;

                           

                            if (i-1<0 || a[i-1][j] == -1)      //X边界处理和不可达标记处理

                                   countx = 0;

                            else

                                   countx = a[i-1][j];

 

                            if (j-1<0 || a[i][j-1] == -1)      //Y边界处理和不可达标记处理

                                   county = 0;

                            else

                                   county = a[i][j-1];

 

                            a[i][j] = countx + county;

                     }

              }

              printf("%d/n",a[end.x][end.y]);

       }

       return 0;

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值