題目描述
如圖,A點有一個過河卒,需要走到目標B點。卒行走規則:可以向下、或者向右。同時在棋盤上的任一點有一個對方的馬(如圖中的C點),該馬所在的點和所有跳躍一步可達的點稱爲對方馬的控制點。例如圖中C 點上的馬可以控制9個點(圖中的P1,P2...P8 和C)。卒不能通過對方馬的控制點。
棋盤用座標表示,A點(0,0)、B點(n,m)(n,m 爲不超過20的整數,並由鍵盤輸入),同樣馬的位置座標是需要給出的(約定:C<>A,同時C<>B)。現在要求你計算出卒從A點能夠到達B點的路徑的條數。
輸入
每個測試文件只包含一組測試數據,每組輸入四個整數n,m,x,y。((n,m)表示B點的座標,(x,y)表示對方馬的座標)
輸出
對於每組輸入數據,輸出一個整數,表示路徑的條數。
分析:到終點的路徑是由之前的點一個一個遞推來的,並且每到一個點,是不用管之前的狀態是怎麼來的,只需要用現在的狀態又去不斷遞推後面的狀態,那麼其實也就滿足了最優子結構,並且無後效性,那麼也就想到了用DP來做,dp(i,j)表示從起點到(i,j)這個點的走法有多少種,然後把馬本身和馬的控制範圍的點都打上標記,表明這些點不能走,將dp[0][0]賦初值爲1,然後DP即可。。。其實這種問題很容易也可以想到用遞歸來做,但不能常規遞歸,因爲中間重複計算了很多次,所以要用記憶化搜索。 注意數字較大,用long long 能過。
代碼1 記憶化搜索:
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cctype>
#include<cstring>
#include<utility>
#include<cstdlib>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define Clear(x) memset(x,0,sizeof(x))
#define fup(i,a,b) for(int i=a;i<b;i++)
#define rfup(i,a,b) for(int i=a;i<=b;i++)
#define fdn(i,a,b) for(int i=a;i>b;i--)
#define rfdn(i,a,b) for(int i=a;i>=b;i--)
typedef long long ll;
using namespace std;
const int maxn = 27;
const int inf = 0x3f3f3f3f;
const double pi=acos(-1.0);
const double eps = 1e-3;
int vis[maxn][maxn];
ll f[maxn][maxn];
int n,m,x,y;
int read()
{
char ch=getchar();int ret=0,f=1;
while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
return f*ret;
}
ll DFS(int x,int y)
{
if(f[x][y]) return f[x][y];
if(x>n||y>m) return 0;
if(x==n&&y==m) return 1;
if(vis[x+1][y]==0&&x+1<=n) f[x][y]+=DFS(x+1,y);
if(vis[x][y+1]==0&&y+1<=m) f[x][y]+=DFS(x,y+1);
return f[x][y];
}
int main()
{
n=read(),m=read(),x=read(),y=read();
Clear(f);
Clear(vis);
vis[x][y]=1;
vis[x-1][y-2]=1,vis[x-2][y-1]=1,vis[x-2][y+1]=1,vis[x-1][y+2]=1;
vis[x+1][y+2]=1,vis[x+2][y+1]=1,vis[x+2][y-1]=1,vis[x+1][y-2]=1;
cout<<DFS(0,0)<<endl;
return 0;
}
代碼2 dp:
#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<cctype>
#include<cstring>
#include<utility>
#include<cstdlib>
#include<iomanip>
#include<iostream>
#include<algorithm>
#define Clear(x) memset(x,0,sizeof(x))
#define fup(i,a,b) for(int i=a;i<b;i++)
#define rfup(i,a,b) for(int i=a;i<=b;i++)
#define fdn(i,a,b) for(int i=a;i>b;i--)
#define rfdn(i,a,b) for(int i=a;i>=b;i--)
typedef long long ll;
using namespace std;
const int maxn = 27;
const int inf = 0x3f3f3f3f;
const double pi=acos(-1.0);
const double eps = 1e-3;
int vis[maxn][maxn];
ll dp[maxn][maxn];//表示從起點走到(i,j)處有幾條路徑
int dir[][2]={{0,0},{-1,-2},{-2,-1},{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2}};
int n,m,x,y;
int read()
{
char ch=getchar();int ret=0,f=1;
while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=getchar();}
return f*ret;
}
void mark()
{
for(int i=0;i<9;i++)
{
if(x+dir[i][0]>=0&&x+dir[i][0]<=n&&y+dir[i][1]>=0&&y+dir[i][1]<=m)
vis[x+dir[i][0]][y+dir[i][1]]=1;
}
}
void slove()
{
dp[0][0]=1;
for(int i=0;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(vis[i][j]) continue;
if(i) dp[i][j]+=dp[i-1][j];//如果不在第一行就要加上上面一行遞推過來的路徑數
if(j) dp[i][j]+=dp[i][j-1];//如果不在第一列就要加上上面一行遞推過來的路徑數
}
}
/**
可以打出路徑便於理解
for(int i=0;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
printf("%d ",dp[i][j]);
}
printf("\n");
}*/
printf("%lld\n",dp[n][m]);
}
int main()
{
n=read(),m=read(),x=read(),y=read();
Clear(dp);
Clear(vis);
mark();
slove();
return 0;
}