1. 程式人生 > >【牛客 - 301哈爾濱理工大學軟體與微電子學院第八屆程式設計競賽同步賽(高年級)】小樂樂下象棋(記憶化搜尋dp,dfs)

【牛客 - 301哈爾濱理工大學軟體與微電子學院第八屆程式設計競賽同步賽(高年級)】小樂樂下象棋(記憶化搜尋dp,dfs)

題幹:
 

小樂樂一天天就知道玩,這一天又想玩象棋。
我們都知道馬走日。
現在給定一個棋盤,大小是n*m,把棋盤放在第一象限,棋盤的左下角是(0,0),右上角是(n - 1, m - 1);
小樂樂想知道,一個馬從左下角(0, 0)開始,走了k步之後,剛好走到右上角(n - 1, m - 1)的方案數。

輸入描述:

輸入:多組樣例輸入,每組一行,三個整數n, m, k(1 <= n, m, k <= 200),如題目所示。

輸出描述:

輸出:輸出答案 mod 1000000007

示例1

輸入

複製

4 4 2

輸出

複製

2

解題報告:

   跪了一晚上發現是因為寫成了ty=x+ny[k]了,,,看來是太久不寫地圖的dfs了、、老毛病又犯了、、難受難受

AC程式碼1:(記憶化搜尋)

  其實也可以直接在main函式中dp[n][m][0]=1,這樣在寫dfs的時候就不需要特判一下出口了、、總的來說是個不算難的好題、、

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
#define fi first
#define se second
using namespace std;
const int MAX = 2e5 + 5;
ll dp[205][205][205];
const ll mod = 1000000007;
int n,m,t;
int nx[9] = {-2,-1,1,2,2,1,-1,-2};
int ny[9] = {1,2,2,1,-1,-2,-2,-1};
bool ok(int x,int y) {
	if(x>=1&&x<=n&&y>=1&&y<=m) return 1;
	else return 0;
}
ll dfs(int x,int y,int res) {
	if(x == n && y == m && res == 0) return dp[n][m][res]=1;
	if(res <= 0) return 0;
	if(dp[x][y][res]!=-1) return dp[x][y][res];
	ll sum = 0;
	int tx,ty;
	for(int k = 0; k<8; k++) {
		tx = x + nx[k];
		ty = y + ny[k];
		if(ok(tx,ty) == 0) continue;
		//if(res==0) continue;
		sum += dfs(tx,ty,res-1);
		sum %= mod;
	}
	dp[x][y][res] = sum;
	return sum;
	
}
int main()
{
	while(~scanf("%d%d%d",&n,&m,&t)) {
		memset(dp,-1,sizeof dp);
		printf("%lld\n",dfs(1,1,t)%mod);
	}	
	return 0 ;
 }

其中也有一個細節值得注意,,這樣搜尋的正確性,,首先因為你表示的狀態,體現出了剩餘的步數,所以不用怕走過去下一步又走回來這種情況,,因為在狀態設計中這屬於不同的狀態,,所以沒事,,再就是不用怕會出現環,,也就是繞一圈狀態又繞回來了。。原因就是因為你的dfs都是res-1的操作,所以整個就是一個DAG圖,不必要擔心會出現未完成值的重複呼叫。。

AC程式碼2:(三種直接dp)

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pii pair<int,int>
#define x first
#define y second
#define mp make_pair
//#define debug
const LL mod=1e9+7;
LL f[205][205][205];
int fx[8][2]={1,2,1,-2,2,1,2,-1,-1,-2,-1,2,-2,-1,-2,1};
void B(){
    int n,m,K;
    while(cin>>n>>m>>K){
        memset(f,0,sizeof(f));
        f[1][1][0]=1;
        for(int k=0;k<=K;++k){
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                    if(f[i][j][k]){
                        for(int o=0;o<8;++o){
                            if(i+fx[o][0]>=1 && j+fx[o][1]>=1)
                            (f[i+fx[o][0]][j+fx[o][1]][k+1]+=f[i][j][k])%=mod; 
                        }
                    }
                }
            }
        }
        cout<<f[n][m][K]<<endl;
    }
}
int main(){
    B();
    return 0;
}





#include<bits/stdc++.h>
#define mod 1000000007
using namespace std;
int dp[205][205][205];
int dis[8][2]={2,1,2,-1,-2,1,-2,-1,1,2,1,-2,-1,2,-1,-2};
int main()
{
    int n,m,k;
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        memset(dp,0,sizeof(dp));
        dp[1][1][0]=1;
        for(int s=1;s<=k;s++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {
                    int temp=0;
                    for(int h=0;h<8;h++)
                    {
                        int x=i+dis[h][0];int y=j+dis[h][1];
                        if(x>=1&&x<=n&&y>=1&&y<=m)
                        temp+=dp[x][y][s-1],temp%=mod;
                }
                dp[i][j][s]=temp;
    }
    printf("%d\n",dp[n][m][k]);
}
    return 0;
}




#include <bits/stdc++.h>
using namespace std;
int dp[205][205][205];
const int mod=1000000007;
int nex[8][2]={{1,2},{2,1},{-1,-2},{-2,-1},{2,-1},{1,-2},{-1,2},{-2,1}};
int main()
{
    int n,m,d;
    while(~scanf("%d %d %d",&n,&m,&d))
    {
        memset(dp,0,sizeof(dp));
        dp[1][1][0]=1;
        for(int k=1;k<=d;k++){
            for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                for(int s=0;s<8;s++){
                    int x=i+nex[s][0];
                    int y=j+nex[s][1];
                    if(x<1||y<1||x>n||y>m) continue;
                    dp[i][j][k]=(dp[i][j][k]+dp[x][y][k-1])%mod;
                }
            }
        }
        printf("%d\n",dp[n][m][d]);
    }
    return 0;
}

看了dp的程式碼發現dp好像也不是很難寫、、、也不是很難想,,因為這種狀態也是符合遞推的。。

簡單的一個題,,著實又讓我對動態規劃有了新的理解!!!!

考慮改編:

    改編成一個n*m的方格,但是終點不是右上角的那個[n,m]點,而是新輸入的兩個點,,這樣進行dp,,但是可能有個問題就是可能搜尋的範圍會大了很多啊。。