區間DP | 羅漢鷹問題

       更新一下今天 “計算理論與算法設計” 期末考試的三道編程題,沒有測試用例的題真是太藍受了。分享一下我考試時的思路,最後也給出了我編寫的自測用例。僅供參考,如有錯誤還請指出~


01

成績 15 開啓時間 2020年06月24日 星期三 15:10
折扣 0.8 折扣時間 2020年06月24日 星期三 18:30
允許遲交 關閉時間 2020年06月24日 星期三 18:30

題目描述:

從前有一羣鷹,它們很喜歡玩疊羅漢,於是就叫它們羅漢鷹。每個羅漢鷹都有一定的翅膀寬度和身高,而身高又分爲兩部分,上半身高度和腿長。每個正在疊羅漢的鷹的上半身高度都會算到整個的高度中,但是隻有最下面那個的腿是露出來的並且算入整體高度。現在它們又要開始疊羅漢了。但是由於某些鷹互爲好朋友,想待在一起聊天,所以每個鷹只願意和它們指定的一些鷹相鄰(相鄰就是指在另一個鷹的上一個或下一個),當然它也可以獨自站在那。你的任務就是給你一羣羅漢鷹,你要從中選出一些來疊羅漢,使得總高度最高。但是爲了美觀起見,鷹在疊羅漢的時候,他們翅膀寬度必須嚴格地從大到小排列,最下面那個最寬。

01

輸入格式:

首先第一行是一個整數T,表示用例組數。

接下來T組用例。每組第一行一個整數n代表給你的鷹數。接下來有n行,第i行首先是3個整數,代表i號鷹的翅膀寬度w,上半身高度h和腿長g。然後有一個數k,代表它願意相鄰的鷹數,之後有k個整數Xj代表它願意和哪k個鷹相鄰。

其中 0 < n <= 500,0 <= w,h,g <= 10000,0 <= k <= n,1 <= Xj<= n。

數據保證任意兩個鷹w不同,並且若i願意和j相鄰,那麼j必願意和i相鄰。

輸出格式:

對每組用例,輸出一行一個整數,代表能達到的最大高度。

 

  測試輸入 期待的輸出 時間限制 內存限制 額外進程
測試用例 1  
  1. 1↵
  2. 2↵
  3. 5 6 7 1 2↵
  4. 6 6 8 1 1↵
 
  1. 20↵
1秒 64M 0

       這道題我是最後一個去做的,一開始瞄了一眼沒有思路就先跳過了。最後再來看這道題,用動態規劃解決的。過了自己的用例感覺沒啥問題~

       首先爲什麼會想到用動態規劃呢?當然是此題很明顯有重疊子問題的性質啦!放屁!明明是後兩題都沒有出現dp所以這道題就試一試dp嘗試思考如何根據子問題推到另一個問題的解,並且如何表達動規變量?當時想了二十多分鐘吧,爲了避免凌亂,就不具體說我是咋想到的了,下面直接擺上我的思路:



1、數據預處理

       首先這道題的數據比較複雜,簡單來看,我們需要儲存:每一個羅漢鷹🦅的寬 weight、高 height、腿長 leg 以及可以相鄰的其他鷹。其實有一個潛在的數據也需要儲存,即標識每一隻鷹的編號,我們記爲 id。爲了方便處理,對每一隻羅漢鷹用結構體來表示,然後定義一個羅漢鷹地結構體數組:

(emmmmm至於鷹爲啥用了 "bird",實在是考試的時候想不起鷹的英文了,我覺得還是比用 “ying” 專業一點)

struct node {
    int id;  //最開始鷹的輸入順序作爲 id
    int width, height, leg; 
    bool match[MAXN];   //match[i] = true表示此鷹可以與id=i的鷹相鄰
} bird[MAXN];

        接着處理輸入,我把輸入的處理寫在一個 Init() 函數中。並且考慮到在後面尋找堆積結果的時候,第一個應該要考慮的就是寬度遞減地堆積,所以說我們應該會需要對寬度進行排序,讓羅漢鷹以 width 遞減的順序儲存在 bird[ ] 中。對結構體以特定的元素排序,可以直接採用 algorithm 庫中的 sort 函數,具體使用方式參考這一篇文章:排序算法 | sort函數的使用。主要注意本題 bird 數組我是從下標1開始使用的。

bool cmp(struct node x, struct node y) {
    return x.width > y.width;
}

void Init() {
    scanf("%d", &n);
    memset(dp, 0, sizeof(dp));   //初始化!

    for (int i = 1; i <= n; i++) {
        bird[i].id = i;
        memset(bird[i].match, false, sizeof(bird[i].match));  //初始化
        
        scanf("%d %d %d", &bird[i].width, &bird[i].height, &bird[i].leg);
        int k, t;
        scanf("%d", &k);
        for (int j = 0; j < k; j++) {
            scanf("%d", &t);
            bird[i].match[t] = true;
        }
    }
    sort(bird + 1, bird + 1 + n, cmp);   //對bird數組以width爲關鍵字排序
}

2、動規思路

       已經將所有的輸入處理好,且按照 width 遞減的順序儲存在 bird 數組中。由於相鄰的鷹要考慮是否能在一起,所以對於子問題中
:最下面的鷹和最上面的鷹是誰是至關重要的。那麼記 dp[ i ][ j ] :以第 i 只鷹作爲最底端,第 j 只鷹作爲最上端的羅漢層高度。(這裏的第幾只表示的是排序後 bird 數組的第幾個)。很容易寫出狀態轉移方程:

dp[i][j+1] = max(dp[i][t] + bird[j+1].height)  

其中:i\leq t\leq jbird[j].match[bird[t].id] == true

        應該很好理解,只是要注意 match 數組中記錄的可相鄰的情況是針對排序之前的順序的,所以應該將當前第 t 個對應到 id 上。

還有初始條件:dp[i][i]=bird[i].leg + bird[i].height

       根據狀態轉移方程,就很容易寫出動態規劃部分的核心代碼:

        int ans = 0;  //最終的最高高度
        /* 依次考慮每一隻鷹作爲最底端 */
        for (int i = 1; i <= n; i++) {
            dp[i][i] = bird[i].leg + bird[i].height;  //初始化
            ans = max(ans, dp[i][i]);
            /* 依次考慮i後每一隻鷹作爲最頂端 */
            for (int j = i + 1; j <= n; j++) {
                dp[i][j] = 0;  //初始化
                /* 在子問題中找到最優的匹配 */
                for (int t = i; t <= j - 1; t++) {
                    if (bird[j].match[bird[t].id])   //合適的纔可以結合
                        dp[i][j] = max(dp[i][j], dp[i][t] + bird[j].height);  
                }
                ans = max(ans, dp[i][j]);
            }
        }
        printf("%d\n", ans);


下面附上完整代碼測試用例

#include <cstdio>
#include <cstring>
#include <algorithm>

#define MAXN 550
using namespace std;

int n;
int dp[MAXN][MAXN] = {0};

struct node {
    int id;  //最開始鷹的輸入順序作爲 id
    int width, height, leg;
    bool match[MAXN];   //match[i] = true表示此鷹可以與id=i的鷹相鄰
} bird[MAXN];

bool cmp(struct node x, struct node y) {
    return x.width > y.width;
}

void Init() {
    scanf("%d", &n);
    memset(dp, 0, sizeof(dp));   //初始化!

    for (int i = 1; i <= n; i++) {
        bird[i].id = i;
        memset(bird[i].match, false, sizeof(bird[i].match));  //初始化

        scanf("%d %d %d", &bird[i].width, &bird[i].height, &bird[i].leg);
        int k, t;
        scanf("%d", &k);
        for (int j = 0; j < k; j++) {
            scanf("%d", &t);
            bird[i].match[t] = true;
        }
    }
    sort(bird + 1, bird + 1 + n, cmp);   //對bird數組以width爲關鍵字排序
}

int main() {
    int t;

    for (scanf("%d", &t); t; t--) {
        Init();
        int ans = 0;  //最終的最高高度
        /* 依次考慮每一隻鷹作爲最底端 */
        for (int i = 1; i <= n; i++) {
            dp[i][i] = bird[i].leg + bird[i].height;  //初始化
            ans = max(ans, dp[i][i]);
            /* 依次考慮i後每一隻鷹作爲最頂端 */
            for (int j = i + 1; j <= n; j++) {
                dp[i][j] = 0;  //初始化
                /* 在子問題中找到最優的匹配 */
                for (int t = i; t <= j - 1; t++) {
                    if (bird[j].match[bird[t].id])   //合適的纔可以結合
                        dp[i][j] = max(dp[i][j], dp[i][t] + bird[j].height);
                }
                ans = max(ans, dp[i][j]);
            }
        }
        printf("%d\n", ans);
    }

}

測試用例(5組)

輸入:

5
2
5 6 7 1 2
6 6 8 1 1
5
2 3 5 2 4 5
3 5 6 1 3
4 5 6 1 2
5 7 10 2 1 5
6 3 2 2 1 4
1
100 4 6 0
5
2 3 5 3 3 4 5
3 5 6 1 3
4 5 20 2 1 2
5 7 10 2 5 1
6 3 2 2 4 1
2
10 2 7 0
11 3 9 0


正確輸出:

20

20

10

30

12



end 

歡迎關注個人公衆號 雞翅編程 ”,這裏是認真且乖巧的碼農一枚。

---- 做最乖巧的博客er,做最紮實的程序員 ----

旨在用心寫好每一篇文章,平常會把筆記彙總成推送更新~

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章