[Acwing藍橋杯DP] 1212. 地宮取寶
大意描述:有一個 n * m 的矩陣,從左上角到右下角走 ,每經過一個點 ,如果當前位置的寶物價值 大於手裡的最大寶物價值 可以選擇拿或者不拿
求 當走到右下角的時候 手中的寶物恰好為 k 件的 總方案數量 。
資料範圍: 1<=n,m<=50
1<=k<=12
0<=c<=12 // c表示的是每個位置的寶物的價值大小
題目分析:
看到這個題 有很多的約束條件 基本上是融合了2. 01揹包問題 - AcWing題庫 和 1015. 摘花生 - AcWing題庫
考慮從左上到右下 如果到一個點 如果該點的值 大於手裡最大值(一定大於手裡的任意一個價值)記錄一下手裡的最大值
本題還有個限制條件,就是到底右下角的時候,手中的寶物件數是k,所以也要記錄一下手裡有多少個寶物
那麼我們用閆氏DP分析法來分析一下
(DP分析自:www.acwing.com/solution/content/7116/)偷個懶QAQ!
這個題的難點在於:狀態計算部分,聯想到01揹包問題,對於每個物品,都有取或不取兩種選擇,而不取是肯定可以的,要取的話,要滿足揹包能放下這個條件
初始化比較簡單 就是 初始化剛開始的起點
檢視程式碼
f[1][1][1][g[1][1]]=1;//這是剛開始起點取的情況 f[1][1][0][0]=1;//這是剛開始起點不取的情況
我們可以注意到 起點不取這個物品的時候 初始化該點的最大價值是0 這裡是用了個小技巧 用0代表什麼都沒有的初始狀態
所以我們要將所有的物品價值遞增 範圍就變成了1~13 ,這就需要在剛開始讀取價值時候 將價值加1 。這樣的化,如果後面的物品價值是0時候也能取
因為我們記錄的是方案數,只關心各個物品之間的大小關係,具體數值不影響答案,但是這樣的做法可以把0作為一個特殊邊界來處理。
(當然也可以賦-1,不同的理解)
那麼這道題基本上就解決了
程式碼:
檢視程式碼
#include <bits/stdc++.h> using namespace std; const int N=55,M=13,MOD=1e9+7; int f[N][N][M][M+1]; int n,m,k; int g[N][N]; int main() { cin>>n>>m>>k; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { cin>>g[i][j]; g[i][j]++; } } f[1][1][1][g[1][1]]=1; f[1][1][0][0]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(i==1&&j==1)continue; for(int u=0;u<=k;u++) { for(int v=0;v<=M;v++) { int &val=f[i][j][u][v]; val=(val+f[i-1][j][u][v])%MOD; val=(val+f[i][j-1][u][v])%MOD; if(u>0&&v==g[i][j]) { for(int c=0;c<v;c++) { val=(val+f[i-1][j][u-1][c])%MOD; val=(val+f[i][j-1][u-1][c])%MOD; } } } } } } int res=0; for(int i=0;i<=M;i++) { res=(res+f[n][m][k][i])%MOD; } cout<<res<<endl; return 0; }