• 过河卒(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
    转载请联系作者
     
     
  • 相关阅读:
    c#基础练习
    一款很厉害的死循环代码
    文字变色逐个出现的特效源码
    IOS开发之UILabel动态高度设置方法
    慎重选择容器类型
    Mac下显示隐藏文件 以及修改 hosts文件内容
    SharePoint 如何使自己的网页自动跳转
    位置和地图:地图的使用
    谈话Java在ThreadLocal理解类
    Android 滑动界面实现---Scroller类别 从源代码和开发文档了解(让你的移动布局)
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/10329614.html
Copyright © 2020-2023  润新知