題目描述:
X 國王有一個地宮寶庫。是 n x m 個格子的矩陣。每個格子放一件寶貝。每個寶貝貼着價值標籤。
地宮的入口在左上角,出口在右下角。
小明被帶到地宮的入口,國王要求他只能向右或向下行走。
走過某個格子時,如果那個格子中的寶貝價值比小明手中任意寶貝價值都大,小明就可以拿起它(當然,也可以不拿)。
當小明走到出口時,如果他手中的寶貝恰好是k件,則這些寶貝就可以送給小明。
請你幫小明算一算,在給定的局面下,他有多少種不同的行動方案能獲得這k件寶貝。
【數據格式】
輸入一行3個整數,用空格分開:n m k (1<=n,m<=50, 1<=k<=12)
接下來有 n 行數據,每行有 m 個整數 Ci (0<=Ci<=12)代表這個格子上的寶物的價值
要求輸出一個整數,表示正好取k個寶貝的行動方案數。該數字可能很大,輸出它對 1000000007 取模的結果。
例如,輸入:
2 2 2
1 2
2 1
程序應該輸出:
2
再例如,輸入:
2 3 2
1 2 3
2 1 5
程序應該輸出:
14
資源約定:
峯值內存消耗 < 256M
CPU消耗 < 1000ms
分析:這道題看到的第一反應就是搜索,但是直接搜索不剪枝的話肯定會超時,所以我們採用記憶化搜索。就是搜索和動態規劃相結合的而形成的一種折中的方法。動態規劃會遍歷所有的狀態,而搜索則可以排除一些無效狀態。
下面就是推導狀態轉移方程了,這個題的子問題還是比較明顯的:就是分兩種大的情況,如果寶貝大於手中的價值,那麼可以拿或者不拿,而如果小於則不拿。
另外要注意的地方:①寶物的價值可以爲0,所以一開始搜索的時候,我們要將手中的價值設置爲負數
②有可能某條路徑是不滿題意的,即無解,那麼此時dp就是0,所以我們要初始化dp爲-1,
以便把沒搜索過的情況和搜索過但是無解的情況區分開來
好了,看代碼吧:
import java.util.*;
public class Main {
static Scanner in = new Scanner(System.in);
static int n,m,k,mod = 1000000007;
static int[][][][] dp = new int[55][55][15][15];
static int[][] mp = new int[55][55];
static int dfs(int x,int y,int sum,int val) {
if(dp[x][y][sum][val+1]!=-1)//搜索過了,直接返回值
return dp[x][y][sum][val+1];
if(x==n-1&&y==m-1) {//到達出口
if(sum==k)//寶物數目滿足,可行
return dp[x][y][sum][val+1] = 1;
if(sum==k-1&&mp[x][y]>val)//少一件,但是這個點的寶物可取
return dp[x][y][sum][val+1] = 1;
return dp[x][y][sum][val+1] = 0;
}
int t = 0;
if(x+1<=n) {//向下走
if(mp[x][y]>val) {//可以拿走
t = (t+ dfs(x+1,y,sum+1,mp[x][y]))%mod;
t%=mod;
}
t = (t+ dfs(x+1,y,sum,val))%mod;//不拿走
t%=mod;
}
if(y+1<=m) {//向右走
if(mp[x][y]>val) {//可以拿走
t = (t+ dfs(x,y+1,sum+1,mp[x][y]))%mod;
t%=mod;
}
t = (t+ dfs(x,y+1,sum,val))%mod;//不拿走
t%=mod;
}
return dp[x][y][sum][val+1] = t % mod;
}
public static void main(String[] args) {
n= in.nextInt();
m = in.nextInt();
k = in.nextInt();
int i,j,p,q;
for(i = 0;i < 55;i++)
for(j = 0;j < 55;j++)
for(p = 0;p < 15;p++)
for(q = 0;q < 15;q++)
dp[i][j][p][q] = -1;
for(i = 0;i < n;i++)
for(j = 0;j < m;j++) {
mp[i][j] = in.nextInt();
}
System.out.println(dfs(0,0,0,-1));
}
}