更新一下今天 “計算理論與算法設計” 期末考試的三道編程題,沒有測試用例的題真是太藍受了。分享一下我考試時的思路,最後也給出了我編寫的自測用例。僅供參考,如有錯誤還請指出~
01
成績 15 開啓時間 2020年06月24日 星期三 15:10 折扣 0.8 折扣時間 2020年06月24日 星期三 18:30 允許遲交 否 關閉時間 2020年06月24日 星期三 18:30 題目描述:
從前有一羣鷹,它們很喜歡玩疊羅漢,於是就叫它們羅漢鷹。每個羅漢鷹都有一定的翅膀寬度和身高,而身高又分爲兩部分,上半身高度和腿長。每個正在疊羅漢的鷹的上半身高度都會算到整個的高度中,但是隻有最下面那個的腿是露出來的並且算入整體高度。現在它們又要開始疊羅漢了。但是由於某些鷹互爲好朋友,想待在一起聊天,所以每個鷹只願意和它們指定的一些鷹相鄰(相鄰就是指在另一個鷹的上一個或下一個),當然它也可以獨自站在那。你的任務就是給你一羣羅漢鷹,你要從中選出一些來疊羅漢,使得總高度最高。但是爲了美觀起見,鷹在疊羅漢的時候,他們翅膀寬度必須嚴格地從大到小排列,最下面那個最寬。
輸入格式:
首先第一行是一個整數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↵
- 2↵
- 5 6 7 1 2↵
- 6 6 8 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 數組的第幾個)。很容易寫出狀態轉移方程:
其中:且。
應該很好理解,只是要注意 match 數組中記錄的可相鄰的情況是針對排序之前的順序的,所以應該將當前第 t 個對應到 id 上。
還有初始條件:
根據狀態轉移方程,就很容易寫出動態規劃部分的核心代碼:
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,做最紮實的程序員 ----
旨在用心寫好每一篇文章,平常會把筆記彙總成推送更新~