DP動態規劃-爬塔(雙層dp)
DP動態規劃-爬塔(雙層dp)
比賽來源:牛客 - 中國計量大學現代科技學院第四屆“中競杯”程式設計校賽
題目--------F題
description:
高川最喜歡的遊戲當屬 Slay the Spire,這是一款爬塔遊戲,你需要從一座塔的底部一直爬到頂部,在爬塔的過程中,塔的每一層都有許多的寶物等你來拿。
高川從塔的左側開始攀爬,從底部爬到頂部,再從右側從頂部逐步下到底部。塔總共有 n 層,每一層都有很多寶物從左到右排列。在左側攀爬時,他只能從每層的最左邊按順序取寶物,在右側下降時,他只能從每層的最右邊按順序取寶物。每個寶物都有一個價值,他最多拿 m 個寶物,他想知道自己從塔上下來時,最多可以拿的寶物價值和是多少
Sample:
解題思路:
- 從題目中可以看出每一層都是獨立的,每一層中都有一個拿取寶物的順序限制,這也是題目的核心。
- 所以首先,我們設定dpPerLine[i][j]來表示第i層選j個寶藏時,在這層的最大寶藏價值。可以用前字尾和來快速實現。
- 然後,我們那拿到pPerLine後,可以直接去動態規劃總共選j個寶物時的最大價值dp[j],最後得到最後的解dp[m]。具體看程式碼註釋,這裡注意j和k要逆序遍歷,因為要保證每一層拿少數的狀態值不會去影響拿多數時的狀態值,不然則可能會出現連續拿一層同一個高價值寶藏的情況。具體情況可以自己去試一試。
原始碼與註釋:
#include<iostream>
#include<algorithm>
using namespace std;
int c[110][110];//寶藏權重
int pre[110][110], suf[110][110];//每一層的字首和、字尾和
int x[110];//每一層的寶藏數量
int dpPerLine[110][110];//第i層選j個寶藏時,在這層的最大寶藏價值。
int dp[10010];//拿i個寶藏的最大價值
int main()
{
int n, m;//塔層數,寶藏可選數量
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> x[i];
for (int j = 1; j <= x[i]; j++) {
cin >> c[i][j];
pre[i][j] = pre[i][j - 1] + c[i][j];//計算字首和
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <=x[i]; j++)
suf[i][j] = pre[i][x[i]] - pre[i][x[i]-j];//計算字尾和
}
//dp第i層選j個寶藏時,在這層的最大寶藏價值。
for (int i = 1; i <= n; i++) {//第i層
for (int j = 1; j <= x[i]; j++) {//在i層選j個寶藏
for (int k = 0; k <= j; k++) {//此時方案為字首選k個
dpPerLine[i][j] = max(dpPerLine[i][j], pre[i][k] + suf[i][j - k]);
}
}
}
//num動態表示此時最多可以拿到寶藏的數量
int num = 0;
for (int i = 1; i <= n; i++) {//遍歷至i層(根據資料讀取時的順序)
num += x[i];
//此時一共選j個寶藏,
//j一定小於 總共可選數量上限m 和 現在可選數量上限num
for (int j = min(num, m); j >=1 ;j--) {
//我們選擇在這一層選k個寶藏,遍歷k
//k一定小於 本層最大寶藏數量x[i] 和 此時一共選的寶藏數j
for (int k = min(x[i],j); k>=1 ; k--) {
//此時共選j個數量,分別是來自這一層的k個和前面的j-k個
dp[j] = max(dp[j] , dp[j - k] + dpPerLine[i][k]);
}
}
}
cout << dp[m] << endl;
return 0;
}
純原始碼:
#include<iostream>
#include<algorithm>
using namespace std;
int pre[110][110],suf[110][110],c[110][110];
int x[110];
int dpPerLine[110][110],dp[10010];
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> x[i];
for (int j = 1; j <= x[i]; j++) {
cin >> c[i][j];
pre[i][j] = pre[i][j - 1] + c[i][j];
}
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= x[i]; j++)
suf[i][j] = pre[i][x[i]] - pre[i][x[i] - j];
for (int i = 1; i <= n; i++)
for (int j = 1; j <= x[i]; j++)
for (int k = 0; k <= j; k++)
dpPerLine[i][j] = max(dpPerLine[i][j], pre[i][k] + suf[i][j - k]);
int num = 0;
for (int i = 1; i <= n; i++) {
num += x[i];
for (int j = min(num, m); j >= 1; j--)
for (int k = min(x[i], j); k >= 1; k--)
dp[j] = max(dp[j], dp[j - k] + dpPerLine[i][k]);
}
cout << dp[m] << endl;
return 0;
}
結果:
可以看到這個方法速度還是比較快的,但和16+ms的大佬比還是比不了der,如果覺得這個方法太繁瑣可以去比賽裡面看看大佬們的程式碼,不過這個方法對於加深dp的理解還是有意義的。
相關文章
- 動態規劃(DP)動態規劃
- [動態規劃] 區間 dp動態規劃
- (C++)DP動態規劃C++動態規劃
- 動態規劃篇——線性DP動態規劃
- 動態規劃 區間dp 基礎題動態規劃
- 【動態規劃】樹形DP完全詳解!動態規劃
- DP 動態規劃入門 一維陣列動態規劃陣列
- 動態規劃演算法(DP)學習<1>動態規劃演算法
- 強化學習(三)用動態規劃(DP)求解強化學習動態規劃
- 迴文串問題(動態規劃DP C++)動態規劃C++
- 【動態規劃】一些奇怪的DP題目的列表動態規劃
- 動態規劃之經典數學期望和概率DP動態規劃
- 動態 DP
- 動態規劃入門——動態規劃與資料結構的結合,在樹上做DP動態規劃資料結構
- 動態規劃——用二進位制表示集合的狀態壓縮DP動態規劃
- 雙序列動態規劃動態規劃
- 以最長公共子序列問題理解動態規劃演算法(DP)動態規劃演算法
- c++ 動態規劃(數塔)C++動態規劃
- 【LeetCode動態規劃#12】詳解買賣股票I~IV,經典dp題型LeetCode動態規劃
- 動態規劃入門 E – 免費餡餅 (dp的另一個應用)動態規劃
- 動態規劃(介紹閆氏dp分析法及相關例題分析)動態規劃
- SPOJ GSS3 (動態dp)S3
- 動態dp複習筆記筆記
- dp 套 dp(dp of dp)小記
- 動態dp & 矩陣加速遞推矩陣
- DP套DP
- [DP] 數位DP
- 【DP】Educational DP Contest
- dp套dp 隨寫
- 【演算法學習筆記】動態規劃與資料結構的結合,在樹上做DP演算法筆記動態規劃資料結構
- 動態規劃動態規劃
- 【DP】區間DP入門
- 簡易狀態壓縮DP
- dp
- [DP] DP最佳化總結
- [leetcode] 動態規劃(Ⅰ)LeetCode動態規劃
- 動態規劃法動態規劃
- 模板 - 動態規劃動態規劃