洛谷 P1002 [NOIP2002 普及組] 過河卒
阿新 • • 發佈:2022-03-25
題目連結 https://www.luogu.com.cn/problem/P1002
一道入門的dp問題。
初始位置為(0,0),有點兒麻煩,改為(1,1),所以所有座標的位置全部+1。
卒到達的每一個位置都是從此位置的上方和左方走過來的。那麼假設卒從(1,1)到達此點的左方點的路徑有x條,到達此點的上方點的路徑有y條,則到達此點的路徑有x+y條。
那麼可以得到狀態轉移方程:dp[i][j]=dp[i-1][j]+dp[i][j-1],所以最終的答案被儲存在dp[n][m](假設B的座標為(n,m))。
需要注意的是需要對dp[1][1]或dp[2][0]進行初始化=1,否則無論走到哪個點其結果都是0。(令dp[2][1]=1按道理也是可以的,我也是這麼寫的)。
接下來開始迴圈計算到達每個點有幾條路徑。當遇到馬和馬能到達的點時就跳過。在這之前需要將馬所在的點和馬能到達的位置初始化,用來判斷。
放AC的程式碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 int bx,by,mx,my; 4 long long int dp[40][40]; 5 int s[40][40]; 6 int main() 7 { 8 cin>>bx>>by>>mx>>my; 9 bx+=2; by+=2; mx+=2; my+=2;//防止越界,並且假設A點為(1,1) 10 dp[2][1]=1; 11 s[mx][my]=1;//標記馬的位置以及馬能到達的點 12 s[mx-2][my-1]=1; s[mx-2][my+1]=1; 13 s[mx-1][my-2]=1; s[mx-1][my+2]=1; 14 s[mx+1][my-2]=1; s[mx+1][my+2]=1; 15 s[mx+2][my-1]=1; s[mx+2][my+1]=1; 16 for(int i=2;i<=bx;i++) 17 { 18 for(int j=2;j<=by;j++)19 { 20 if(s[i][j]) continue;//如果此點被標記則跳過 21 dp[i][j]=dp[i-1][j]+dp[i][j-1];//狀態轉移方程 22 } 23 } 24 cout<<dp[bx][by]; 25 return 0; 26 }
當然,還可以優化。用滾動陣列可以節省空間。
因為我們只用到第i行和第i-1行的答案,所以可以只定義dp[2][1]就好了。
如何只保留第i和第i-1行的答案?取模。i=>i%2,i-1=>(i-1)%2。
此外,因為是滾動陣列 , 所以如果當前位置被馬攔住了一定要記住清零。
另,學到了一個新知識:
x%2可以在程式碼中寫成更快的運算方式x&1。
如果x是偶數,那麼x&1=0,如果x是奇數,那麼x&1=1。
放AC程式碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 int bx,by,mx,my; 4 long long dp[40][40]; 5 int s[40][40]; 6 int main() 7 { 8 cin>>bx>>by>>mx>>my; 9 bx+=2; by+=2; mx+=2; my+=2; 10 dp[1][2]=1; 11 s[mx][my]=1; 12 s[mx-2][my-1]=1; s[mx-2][my+1]=1; 13 s[mx-1][my-2]=1; s[mx-1][my+2]=1; 14 s[mx+1][my-2]=1; s[mx+1][my+2]=1; 15 s[mx+2][my-1]=1; s[mx+2][my+1]=1; 16 for(int i=2;i<=bx;i++) 17 { 18 for(int j=2;j<=by;j++) 19 { 20 if(s[i][j]) 21 { 22 dp[i&1][j]=0; 23 continue; 24 } 25 dp[i&1][j]=dp[(i-1)&1][j]+dp[i&1][j-1]; 26 } 27 } 28 cout<<dp[bx&1][by]; 29 return 0; 30 }