1.1馬攔過河卒

馬攔過河


【問題描述】

棋盤上A點有一個過河卒,需要走到目標B點。卒行走的規則:可以向下、或者向右。同時在棋盤上C點有一個對方的馬,該馬所在的點和所有跳躍一步可達的點稱爲對方馬的控制點。因此稱之爲“馬攔過河卒”。

棋盤用座標表示,A(0, 0)B(n, m)(n, m爲不超過15的整數),同樣馬的位置座標是需要給出的。現在要求你計算出卒從A點能夠到達B點的路徑的條數,假設馬的位置是固定不動的,並不是卒走一步馬走一步。

【輸入】

一行四個數據,分別表示B點座標和馬的座標。

【輸出】

一個數據,表示所有的路徑條數。

【樣例】

knight.in knight.out

6 6 3 36


【解析】

        這是一個看似簡單,實則是不那麼簡單的題,現在我只會簡單的方法,等我有空把再把不簡單的方法整出來,簡單的方法就是遞歸,款搜、深搜

         遞歸算法:

pascal代碼

var
    n,m,horse_x,horse_y:integer;
    k:longint;
    i,j:integer;
   control:array[-2..17,-2..17] of boolean;
   a:array[1..8,1..2] of integer=((-1,-2),(-2,-1),(1,2),(2,1),(-1,2),(2,-1),(-2,1),(1,-2));
procedure try(x,y:integer);
begin
    if (x=n) and (y=m) then
       begin
           inc(k);
          exit;     end;
    if (x+1<=n) and not control[x+1,y] then try(x+1,y);
   if (y+1<=m) and not control[x,y+1] then try(x,y+1);
end;
begin
    readln(n,m,horse_x,horse_y);
    fillchar(control,sizeof(control),0);
    k:=0;
    for i:=1 to 8 do control[horse_x+a[i,1],horse_y+a[i,2]]:=true;
    control[horse_x,horse_y]:=true;
    try(0,0);
    writeln(k);    
end.

C++代碼:

#include <iostream>
using namespace std;
int n,m,horse_x,horse_y;
int i,j,k;
int a[8][2]={{-1,-2},{-1,2},{-2,-1},{-2,1},{1,-2},{1,2},{2,-1},{2,1}};
bool control[16][16]={0};
void horse(int x,int y)
{
     if ((x==n)&&(y==m))
     {
        ++k;
        return;                     
     }
     if ((x+1<=n) && (!control[x+1][y])) {horse(x+1,y);}
     if ((y+1<=m) && !control[x][y+1] ){horse(x,y+1);}
     };
int main()
{
    cin>>n>>m>>horse_x>>horse_y;
    for (i=0;i<8;i++)
    {
        if ((horse_x+a[i][0]<=n)&&(horse_x+a[i][0]>=0)&&(horse_y+a[i][1]<=m)&&(horse_y+a[i][1]>=0))
           control[horse_x+a[i][0]][horse_y+a[i][1]]=true;
        }
    control[horse_x][horse_y]=true;
    horse(0,0);
    cout<<k<<endl;
    system("pause");
    return 0;
    }

        上面兩種寫法都是遞歸,但是兩者在細節上處理上有點小不同,如c++代碼裏的紅色那塊加了個限制條件,而pascal裏沒有,那是因爲我在pascal裏定義control數組時故意把範圍擴大了,所以不存在溢出的問題,而c++的下標只能從0開始,所以必須加上紅色的限制條件,不然很可能出現數組越界的問題
        遞歸調用的方法,可想而知效率肯定不會理想,但是這個題其實可以優化到n^2!!對了,此題有個條件和經典的動歸數字三角形有驚人相似之處“卒行走的規則:可以向下、或者向右”,所以當前狀態時有其左邊和上邊得來,但是有點麻煩的是有個噁心的馬,那怎麼辦呢,簡單,直接把這個馬的控制點全置爲零不就解決問題了嗎??所以動歸方程爲:f[i][j]=f[i-1][j]+f[i][j-1],f[i][j]表示從起點能到達(i,j)的路徑條數,不過這題要注意要處理邊界條件,即當i-1或j-1小於零是的狀態,同時f[0][0]=1。代碼如下只寫了c++代碼,

#include <iostream>
using namespace std;
int n,m,horse_x,horse_y;
int i,j,k;
bool flag[16][16];
int a[8][2]={{-1,-2},{-1,2},{-2,-1},{-2,1},{1,-2},{1,2},{2,-1},{2,1}};
int f[16][16]={0};
int main()
{
    cin>>n>>m>>horse_x>>horse_y;
    memset(flag,true,sizeof(flag));
    flag[horse_x][horse_y]=false;
    for(i=0;i<8;i++)
    {
       if ((horse_x+a[i][0]<=n)&&(horse_x+a[i][0]>=0)&&(horse_y+a[i][1]>=0)&&(horse_y+a[i][1]<=m))
       {
          flag[horse_x+a[i][0]][horse_y+a[i][1]]=false;
                                                                                                  }
                    }
    f[0][0]=1;
    for (i=0;i<=n;i++)
    {
        for(j=0;j<=m;j++)
        {
           if (i-1<0&&j-1>=0&& flag[i][j]) f[i][j]=f[i][j-1];
           if (i-1>=0&& j-1<0 && flag[i][j]) f[i][j]=f[i-1][j];
           if(i-1>=0&&j-1>=0&&flag[i][j]) f[i][j]=f[i-1][j]+f[i][j-1];
                         }
        }  
      cout<<f[n][m]<<endl;
      system("pause");
      return 0;  
    }
    雖然時間效率上去了,如果n,m過大的話注意要用高精度,不然……&……

    
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章