1. 程式人生 > >NOIP 2002 馬攔過河卒 BFS+遞推

NOIP 2002 馬攔過河卒 BFS+遞推

Description :

如圖,A點有一個過河卒,需要走到目標B點。卒行走的規則:可以向下、或者向右。

同時在棋盤上的任一點有一個對方的馬(如上圖的C點),該馬所在的點和所有跳躍一步可達的點稱為方馬的控制點。例如上圖C點上的馬可以控制9個點(圖中的P1,P2...P8和C)。卒不能通過對方的控制點。
棋盤用座標表示,A點(0,0)、B點(n, m)(n,m為不超過20的整數,並由鍵盤輸入),同樣馬的位置座標是需要給出的(約定:C≠A,同時C≠B)。現在要求你計算出卒從A點能夠到達B點的路 徑的條數。


Input

B點的座標(n,m)以及對方馬的座標(X,Y) {不用判錯}

Output

一個整數(路徑的條數)。

Sample Input
6 6 3 2

Sample Output
17

本題要求的是方案數  一般的想法是直接廣搜  每到終點一次ans+1  然而這樣需要耗費大量時間

經過仔細分析題目發現 :以為卒只能向右或向下 所以每個能到達的點的ans值都只和它左邊和上面那個

點有關 所以能得到 ans[x][y]=ans[x-1][y]+ans[x][y-1]這樣的遞推公式

程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct node{
  int x,y;
};
int fx[2][2]={1,0,0,1};
int mfx[8][2]={-2,-1,-2,1,2,-1,2,1,1,2,-1,2,1,-2,-1,-2};
long long vis[30][30];
int m,n,ex,ey;
void init()  //初始化
{
    for(int i=0;i<30;i++)
        for(int j=0;j<30;j++)
        vis[i][j]=0;
    return;
}
long long pd(int a,int b)
{
    if(a<0||b<0)
        return 0;
    if(vis[a][b]==-1)
        return 0;
    return vis[a][b];
}
long long bfs()
{
    int i;
    queue<node>q;
    node now;
    int tx,ty;
    for(i=0;i<8;i++)  //馬控制的區域
    {
        tx=m+mfx[i][0];
        ty=n+mfx[i][1];
        if(tx<0||ty<0)
            continue;
        vis[tx][ty]=-1;
    }
    vis[m][n]=-1;
    q.push((node){0,0});
    vis[0][0]=1;
    while(!q.empty())
    {
        now=q.front();
   //     cout<<now.x<<' '<<now.y<<' '<<vis[now.x][now.y]<<endl;
        q.pop();
        for(i=0;i<=1;i++) //兩種走法
        {
            tx=now.x+fx[i][0];
            ty=now.y+fx[i][1];
            if(vis[tx][ty]!=0||tx>ex||ty>ey)     //不走重複的路(剪枝) ||  超過終點了 不能返回  所也不可能(剪枝)
                continue;
            vis[tx][ty]=pd(tx-1,ty)+pd(tx,ty-1);   //遞推 (前值的下標小於0或 被馬控制 則返回的值是0)
            q.push((node){tx,ty});
        }
    }
    return vis[ex][ey];
}
int main()
{
    scanf("%d%d%d%d",&ex,&ey,&m,&n);
        init();
        printf("%lld\n",bfs());
    return 0;
}