經典dp問題 矩陣取數以及變形

2021-08-18 20:07:12 字數 3726 閱讀 3497

再次總結一些比較經典的在矩陣中確定乙個起始狀態和結束狀態, 問從起始到結束中走過的數要得到, 問最小(大), 路徑輸出等問題. 這些問題都可以轉化為dp模型來解決. 下面以一些我做過的題進行分析.

入門級: 51nod - 1083

問題: 給定乙個二維矩陣, 問從左上角走到右下角可以取到的最大值是多少. 每次只能往下或者往右走.

這道題就非常簡單了, dp[i][j] 代表走到(i, j)這個點的最大值是多少, 轉移方程就是

dp[i][j] = dp[i-1][j] + dp[i][j-1]; o(n^2)的複雜度.

ac code

const int maxn = 5e2+5;

int a[maxn][maxn], dp[maxn][maxn];

void solve()

}for (int i = 1 ; i <= n ; i ++)

}cout << dp[n][n] << endl;

}

變形題 洛谷p1002

題意: 就是在乙個二維矩陣中, 有一匹馬, 馬一次能跳到的點是他的控制點, 問從(0, 0)出發到達所給的終點的路徑有多少條. 每次只能向下或者右走.

依舊dp, dp[i][j] 代表到達這個點的路徑條數, 那麼它依舊只能被上走兩個進行更新, 只不過我們需要處理下馬搜控制的點判一下不進行更新即可. 轉移方程類似

ac code

const

int maxn = 50+5;

int a[maxn][maxn];

ll dp[maxn][maxn];

int dx = ;

int dy = ;

void solve()

a[rx][ry] = -1;

dp[0][0] = 1;

for (int i = 0 ; i <= 20 ; i ++)

if (dp[ex][ey]) break;

}printf("%lld\n", dp[ex][ey]);

}

變形二: uva - 10285

題意: 還是乙個二維矩陣, 可以從任意乙個點出發或者結束, 對應上面的點代表該點的海波高度, 每次只能向海拔高度低的走, 問可以走的最長的路有多長. 可以往四個方向走.

思路: 還是dp, dp[i][j] 代表走到這個點的最長路徑是多少, 那麼因為起始點可以任意, 所以我們需要進行一波預處理, 即我們把矩陣中的每乙個數和位置先取出來, 然後按照從小到大進行排序, 每次取出來乙個就進行在原圖上的dp, 即判斷四個方向中如果有比當前點小的點並且可以進行更新(即更優路徑), 那麼我們就更新這個點即可, 最後在所有的點中取乙個max即可

ac code

const

int maxn = 1e2+5;

int dx = ;

int dy = ;

struct node

}e[maxn*maxn];

int a[maxn][maxn], dp[maxn][maxn];

void solve()

; }

}sort(e+1, e+1+k); int ans = 0;

for (int i = 1 ; i <= k ; i ++) }}

cout

<< s << ": "

<< ans << endl;

}}

經典問題 : uva116

(這道題非常經典, 一定要記住這道題!)

題意: 還是乙個二維矩陣, 每個點上有數, 從這個矩陣的第一列的任意位置出發到達最後一列的任意位置結束, 每次可以向下一列的上面一行, 當前行, 下面一行走, 並且注意在第一行時向上面一行走是走到最後一行, 同理最後一行也是, 問能取到的最小值是多少, 並且輸出路徑, 即每一列的行數, 如果有多解, 那麼輸出字典序最小的答案.

思路: 跟矩陣取數有點類似, 所以我們可以設dp[i][j]代表走到這個(i, j)的最小值是多少, 注意到最後要輸出路徑, 當然很簡單記錄一下此時的狀態從哪個狀態轉移過來的即可, (但是我正著寫死活過不了, 希望路過的大神貼乙份正著寫的**給我學習學習…), 所以我們倒著推, 即從結束狀態往起始狀態推, 這樣答案肯定不會變的,然後我們處理好邊界問題即可, 和同時處理好字典序的問題, 詳情請看**實現:

ac code

const

int inf = 0x3f3f3f3f; //用這個可以直接mem

const ll inf = 1e18;

const

int mod = 1e9+7;

const

int maxn = 1e2+5;

int a[15][maxn], pre[15][maxn], dp[15][maxn];

int next[15][maxn];

int path[maxn];

void solve()

}int st = 1;

fill(dp, inf); int ans = inf;

for (int i = m ; i >= 1 ; i --) ;

if (j == 1) r[0] = n; // 處理邊界問題

if (j == n) r[2] = 1;

sort(r, r+3); // 這樣就可以保證字典序最小啦.

for (int k = 0 ; k < 3 ; k ++) }}

if (i == 1 && dp[j][i] < ans) ans = dp[j][i], st = j;

// 同樣我們行是從小列舉的, 這樣也就可以保證字典序樂.}}

printf("%d", st);

for (int i = 1 ; i < m ; i ++) // 列印路徑即可

printf("\n%d\n", ans);

}}

相似題: hdu - 5092

經典題的簡化, 幾乎和上面那道題一模一樣, 就是變成了從第一行到最後一行, 並且字典序最大, 最後輸出每一行的列數即可. 那麼會了上面那道, 這道就是隨便寫寫樂, 注意保持字典序最大即可.

這還是上海邀請賽的題, 應該是改編上面那道的, 所以記住經典模型是多麼重要的啊……. (當時我用的bfs寫的, 賊麻煩)

ac code

const

int inf = 0x3f3f3f3f; //用這個可以直接mem

const ll inf = 1e18;

const

int mod = 1e9+7;

const

int maxn = 100+5;

int a[maxn][maxn];

int dp[maxn][maxn], next[maxn][maxn];

void solve()

}int st = 1, mi = inf; fill(dp, inf);

for (int i = n ; i >= 1 ; i --)

if (dp[i+1][j] + a[i][j] < dp[i][j])

if (j - 1 >= 1 && dp[i+1][j-1] + a[i][j] < dp[i][j])

}if (i == 1 && dp[i][j] < mi) mi = dp[i][j], st = j;}}

printf("%d", st);

for (int i = 1 ; i < n ; i ++)

簡單DP 矩陣取數問題

乙個n n矩陣中有不同的正整數,經過這個格仔,就能獲得相應價值的獎勵,從左上走到右下,只能向下向右走,求能夠獲得的最大價值。例如 3 3的方格。1 3 3 2 1 3 2 2 1 能夠獲得的最大價值為 11。input 第1行 n,n為矩陣的大小。2 n 500 第2 n 1行 每行n個數,中間用空...

dp 更難的矩陣取數問題

題目 乙個m n矩陣中有不同的正整數,經過這個格仔,就能獲得相應價值的獎勵,先從左上走到右下,再從右下走到左上。第1遍時只能向下和向右走,第2遍時只能向上和向左走。兩次如果經過同乙個格仔,則該格仔的獎勵只計算一次,求能夠獲得的最大價值。例如 3 3的方格。1 3 3 2 1 3 2 2 1 能夠獲得...

多路dp 更難的矩陣取數問題

解題關鍵 1 注意i和j的最大取值都是n,k是i與j的和。2 空間卡的很緊,多一位都不行。轉移方程 dp max a a 通過觀察,可以消去乙個變數,從而 dp k 1 max a a 然後再將相同的處理掉即可 1 include2 using namespace std 3 typedef lon...