1. 程式人生 > >NOIP 2002普及組 過河卒詳解

NOIP 2002普及組 過河卒詳解

本文圖片引用自“kcfzyhq”的部落格

1.分析

首先我們來看看下面這個圖,這個圖基本表現了題目的意思:一個卒要從圖的左上角A點走到右下角B點,而其中有一點C為馬的位置,C與其周邊馬能走到的P1~P8點共9個點是不能走的,問有多少種從A走到B的方法

這裡寫圖片描述

我們可以先把這個問題當數學問題來考慮相信許多朋友以前都遇到過類似的數學問題,對於點[i,j],它的走法數等於它上方點與其左方點走法數之和(因為只能向下或向右走),也就是B[i,j]=B[i-1][j]+b[i][j-1],如下圖就是一個例子

這裡寫圖片描述

但換到有馬阻攔的問題中,單純地這樣搜尋就行不通了,如下圖

這裡寫圖片描述

這張圖所得的答案雖然是正確的,但實際上這樣的操作是錯誤的,圖中藍色的“1”應該改為0,如下圖

這裡寫圖片描述

因為這個位置後面被馬頭擋住,自然是行不通的,值應為0,在這個例子中這裡的值是1還是0對答案沒有影響,但大家可以想象,如果在最左邊一條邊上,一個點上下都是馬能走到的位置,值還為1的話,就會影響它右側點的值(不懂的看下面這個例子)

這裡寫圖片描述

就像上圖,正常情況下紅色點所在的一整列初始賦值都是1,但是紅色點實際值應該為0,如果值仍賦為1,則會導致藍色點的值比實際值大1,從而導致整個結果錯誤。
因此,我們在賦初值時,要專門考慮最上和最左一列的情況,具體方法參見程式碼

哦,對了,這道題的資料可能很大,注意要開long long,否則會炸

2.AC程式碼

#include <iostream>
#include <cstdio> using namespace std; long long B[21][21]; int n,m,a,b; void init() { for (int i=0;i<=n;i++){ //先把所有點都賦為1,剛剛講的特殊情況下面再考慮 for (int j=0;j<=m;j++){ B[i][j]=1; } } if(a-2>=0&&b-1>=0) //把馬的位置和所有馬能走到的位置都賦為0,注意考慮邊界 B[a-2
][b-1]=0; if(a-2>=0&&b+1<=m) B[a-2][b+1]=0; if(a-1>=0&&b-2>=0) B[a-1][b-2]=0; if(a-1>=0&&b+2<=m) B[a-1][b+2]=0; if(a+1<=m&&b-2>=0) B[a+1][b-2]=0; if(a+2<=n&&b-1>=0) B[a+2][b-1]=0; if(a+1<=n&&b+2<=m) B[a+1][b+2]=0; if(a+1<=n&&b+1<=m) B[a+2][b+1]=0; B[a][b]=0; } int main() { scanf("%d%d%d%d",&n,&m,&a,&b); init(); for (int i=0;i<=n;i++){ for (int j=0;j<=m;j++){ if (B[i][j]!=0){ if (i==0 && j==0){ continue; }/*這裡就是處理所說的特殊情況,相當於如果在最上一行或者最左一行 出現一個馬,那麼後面的值都賦為0 */ else if (i==0){ B[i][j]=B[i][j-1]; }else if (j==0){ B[i][j]=B[i-1][j]; }else{ B[i][j]=B[i-1][j]+B[i][j-1]; } } } } printf("%lld\n",B[n][m]); }