过河卒(Noip2002)

【题目描述】

棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的某一点有一个对方的马(如C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点,如图3-1中的C点和P1,……,P8,卒不能通过对方马的控制点。棋盘用坐标表示,A点(0,0)、B点(n, m) (n,m为不超过20的整数),同样马的位置坐标是需要给出的,C≠A且C≠B。现在要求你计算出卒从A点能够到达B点的路径的条数。

【输入】

给出n、m和C点的坐标。

【输出】

从A点能够到达B点的路径的条数。

【输入样例】

8 6 0 4

【输出样例】

1617



例题不怎么详的解:

过河卒,中国象棋用语,过了河的卒可以向前、向左、向右移动,但是单单不能后退的,所以被广泛用来形容‘没有退路’。

也指的一种只能前进不能后退的破釜沉舟的勇气,或者是指做事小心谨慎步步为营的一种人生态度。

 
分析:本题中,卒只能向下和右移动,而不能往回走或往左走。可以很快看出,本题可以用搜索来做。但是根据书上来说,好像无法承受较大的数据规模。
 
以后学完动态规划来把动规算法补上。
 
用递推的方法,我们可以用一个数组储存到某一点的路径总数,另一个数组(bool型)储存整张棋盘,来代表某个位置马是否能够到达。
 
假设用F[I,J]到达点(I,J)的路径数目用G[I,J]表示点(I,J)是否为对方马的控制点,G[I,J]=0表示不是对放马的控制点,G[I,J]=1表示是对方马的控制点。
那么有隐含条件:
 
F[I,J]=0{G[I,J]=1}
 
根据题目,可以看出卒要从左上到右下,那么只能以向下走和向右走的方法到达终点。换句话说,以逆推的思想,棋盘上的每一个点只能从这个点的左边或者上边到达,由此判断,如果在棋盘上面和左边的边缘上有马可以达到的位置,那么从这个点往后的剩下的整行或整列,卒都不能到达。
 
而且,从题目来看,这个卒的行走路径还具有很强的不可逆性,无法回头,很容易注意到,棋子每向右走一步,那么它上一步所在的位置的整列(colum)都将无法到达,得出递推关系式:
 
F[0,J]=F[0,J-1]{J>0,G[0,J]=0}
 
同理,棋子每向下走一步,那么踏上一步所在位置的整行(row)都将无法到达,则有递推关系式:
 
F[I,0]=F[I-1,0]{I>0,G[I,0]=0}
 
这两个关系式都可以看作是用来排除不可能条件的,对真正得出答案没用,缺少了一个核心递推式。
 
要让程序进行下去,最关键的关系式:
 
F[I,J]=F[I-1,J]+F[I,J-1]{I>0,J>0,G[I,J]=0}
 
这里很容易看明白,[i-1][j-1]代表的就是目前点[i,j]左边的点和上面的点总共的可到达路径条数,很明显,其总和就是该点的有效路径。
 
从一开始的很容易看出来的递推边界开始递推:
 
F[0,0]=1
 
即可得出答案。(哎,第一篇写的就这么麻烦,不过挺清楚的。)
 
样例代码如下:
#include<iostream>
#include<cstring>
using namespace std;
long long a[30][30];
int vis[30][30];
int next[][2]={{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};
int main()
{
    int n,m;
    int x,y;
    int nx,ny;
    int i,j;
    
    memset(vis,0,sizeof(vis));
    cin>>n>>m>>x>>y;
    
    a[0][0]=0;//处理A=B的情况
    vis[x][y]=1;//设置马管辖的位置 
    a[x][y]=0;
    for(i=0;i<8;i++)
    {
        nx=x+next[i][0];
        ny=y+next[i][1];
        if(0<=nx&&nx<=n&&0<=ny&&ny<=m)
        {
            vis[nx][ny]=1;
            a[nx][ny]=0;
        }
    }
    for(i=0;i<=n;i++)
    {
        if(vis[i][0]==1)
            while(i<=n)
            	{
                	i++;
                	a[i][0]=0;
            	}
        else 
            a[i][0]=1;
    }
    for(j=0;j<=m;j++)
    {
        if(vis[0][j]==1)
            while(j<=m)
            {
                j++;
                a[0][j]=0;
            }
        else 
            a[0][j]=1;
    }
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            if(vis[i][j]==0)
                a[i][j]=a[i][j-1]+a[i-1][j];
    cout<<a[n][m]<<endl;
    return 0;
} 

 

2019-01-28 11:51:23
转载请联系作者
 
 
posted @ 2019-01-28 11:52  DarkValkyrie  阅读(1072)  评论(0编辑  收藏  举报