【題目描述】
棋盤上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
【來源】
Noip2002
這個題是一道經典的老題一直都想寫一下,終於在一個伸手不見五指的夜晚如願以償,用時2個小時,調試時間過長,主要原因是腦子反應過慢。這個題有個很麻煩的地方是故意把坐標給混亂,想了半天終於有一種好的辦法,就是將原圖向左翻轉90度。如下圖:
這樣就不會混亂,坐標軸就與給定坐標對應。這個題的遞推邊界一定要考慮好,而且馬的控制點這一塊一定要細心,不然很容易出錯。
1 #include<iostream> 2 using namespace std; 3 int n, m, cx, cy; 4 long long f[40][40], g[40][40];//注意盡量使用long long不然容易超出數據范圍 5 int main() 6 { 7 cin>>n>>m>>cx>>cy; 8 f[0][0]=1; 9 //以下考慮馬的控制點有無越界 10 g[cx][cy]=1; 11 if(cx-1>=0&&cy-2>=0)g[cx-1][cy-2]=1; 12 if(cx+1<=n&&cy-2>=0)g[cx+1][cy-2]=1; 13 if(cx-2>=0&&cy-1>=0)g[cx-2][cy-1]=1; 14 if(cx+2<=n&&cy-1>=0)g[cx+2][cy-1]=1; 15 if(cx-2>=0&&cy+1<=m)g[cx-2][cy+1]=1; 16 if(cx+2<=n&&cy+1<=m)g[cx+2][cy+1]=1; 17 if(cx-1>=0&&cy+2<=m)g[cx-1][cy+2]=1; 18 if(cx+1<=n&&cy+2<=m)g[cx+1][cy+2]=1; 19 20 //以下為遞推邊界 21 for(int i=1; i<=n; i++) 22 if(!g[i][0])f[i][0]=f[i-1][0]; 23 for(int j=1; j<=m; j++) 24 if(!g[0][j])f[0][j]=f[0][j-1]; 25 //以下為遞推關系式,相對簡單能想到 26 for(int i=1; i<=n; i++) 27 for(int j=1; j<=m; j++) 28 { 29 if(g[i][j])f[i][j]=0; 30 if(!g[i][j])f[i][j]=f[i][j-1]+f[i-1][j]; 31 } 32 //調試代碼所用 33 // for(int i=0; i<=n; i++) 34 // { 35 // for(int j=0; j<=m; j++)cout<<f[i][j]<<" "; 36 // cout<<endl; 37 // } 38 cout<<f[n][m]; 39 return 0; 40 }
另外這個題應該可以用到搜索,等講到搜索用搜索寫第二種方法,所以本篇文章未完待續。。。。。。
2018年10月2日晚,終於快該講搜索了,決定重新寫一下《過河卒》,用時10分鍾,代碼如下
1 #include<cstdio> 2 using namespace std; 3 int cnt=0;//用於存儲路徑數 4 bool visd[30][30]={0};//用於標記坐標位置是否已經被搜索 0為沒有,1為訪問 5 bool b[30][30]={0};//用於標記坐標點是否為馬的控制點,0為不是,1為是 6 int n, m, cx, cy; 7 int next[2][2]={{1,0},{0,1}}; 8 void dfs(int x, int y) 9 { 10 if(x==n && y==m) 11 { 12 cnt++; 13 return; 14 } 15 for(int i=0; i<2; i++) 16 { 17 int nx=x+next[i][0]; 18 int ny=y+next[i][1]; 19 if((nx>n) || (ny>m) || b[nx][ny])continue;//判斷下一步坐標是否越界 或是馬的控制點 20 if(!visd[nx][ny] ) 21 { 22 visd[nx][ny]=1; 23 dfs(nx,ny); 24 visd[nx][ny]=0; 25 } 26 27 } 28 } 29 int main() 30 { 31 scanf("%d%d%d%d",&n,&m,&cx,&cy); 32 b[cx][cy]=1; 33 if(cx-1>=0&&cy-2>=0)b[cx-1][cy-2]=1; 34 if(cx+1<=n&&cy-2>=0)b[cx+1][cy-2]=1; 35 if(cx-2>=0&&cy-1>=0)b[cx-2][cy-1]=1; 36 if(cx+2<=n&&cy-1>=0)b[cx+2][cy-1]=1; 37 if(cx-2>=0&&cy+1<=m)b[cx-2][cy+1]=1; 38 if(cx+2<=n&&cy+1<=m)b[cx+2][cy+1]=1; 39 if(cx-1>=0&&cy+2<=m)b[cx-1][cy+2]=1; 40 if(cx+1<=n&&cy+2<=m)b[cx+1][cy+2]=1; 41 dfs(0,0); 42 printf("%d",cnt); 43 return 0; 44 }
評測結果如下:
顯然有數據超時,so我們遇到題搜索能得到一個正確的答案,但不一定能AK!需要分析他的復雜性!遞歸轉遞推是灰常重要的一種方法!請認真對比以上兩種算法實現方式!