題目
題目描述
臉哥最近來到了一個神奇的王國,王國裏的公民每個公民有兩個下屬或者沒有下屬,這種關係剛好組成一個 n 層的完全二叉樹。
公民 i 的下屬是 2 * i 和 2 * i +1。最下層的公民即葉子節點的公民是平民,平民沒有下屬,最上層的是國王,中間是各級貴族。
現在這個王國爆發了戰爭,國王需要決定每一個平民是去種地以供應糧食還是參加戰爭,每一個貴族(包括國王自己)是去管理後勤還是領兵打仗。
一個平民會對他的所有直系上司有貢獻度,若一個平民 i 參加戰爭,他的某個直系上司 j 領兵打仗,那麼這個平民對上司的作戰貢獻度爲 wij。
若一個平民i 種地,他的某個直系上司 j 管理後勤,那麼這個平民對上司的後勤貢獻度爲 fij,若 i 和 j 所參加的事務不同,則沒有貢獻度。
爲了戰爭需要保障後勤,國王還要求不多於 m 個平民參加戰爭。國王想要使整個王國所有貴族得到的貢獻度最大,並把這件事交給了臉哥。但不幸的是,臉哥還有很多 deadline 沒有完成,他只能把這件事又轉交給你。你能幫他安排嗎?
輸入格式
第一行兩個數 n;m。
接下來 2^(n-1) 行,每行n-1 個數,第 i 行表示編號爲 2^(n-1)-1+ i 的平民對其n-1直系上司的作戰貢獻度,其中第一個數表示對第一級直系上司,即編號爲 (2^(n-1)-1+ i)/2 的貴族的作戰貢獻度 wij,依次往上。
接下來 2^(n-1)行,每行n-1個數,第i行表示編號爲 2^(n-1)-1+ i的平民對其n-1個直系上司的後勤貢獻度,其中第一個數表示對第一級直系上司,即編號爲 (2^(n-1)-1+ i)/2 的貴族的後勤貢獻度 fij ,依次往上。
輸出格式
一行一個數表示滿足條件的最大貢獻值
題解&分析
還沒有做到這樣的題。。
首先n很小,所以可以先考慮枚舉,但是怎麼枚舉呢?
這是一個完全二叉樹,好像沒有什麼思路
考慮其他方法,不難看出是一個樹形dp,那麼就有dp[i][j]表示以i爲根的子樹選j個葉子打仗的最大貢獻
如果枚舉葉子節點情況呢?很容易發現是超時的
同時我們發現一個葉子只有n-1個祖先,那麼如果將所有的祖先情況枚舉出來呢?
也就是說將每一個非葉子節點的狀態枚舉,是否去打仗
時間複雜度O(n)好像可以接受,再來看轉移
由於樹形DP是要從下往上轉移,而我們枚舉了每個祖先的狀態,其實葉子結點x的dp[x][0]與dp[x][1]都已經出來了,那麼就可像其他dp一樣邊枚舉邊轉移
那麼式子就出來了:
看一下重難點:
主要是要想到怎樣去枚舉狀態,對題目的條件分析要更全面一些
其實可根據輸入方式來進行分析
同時,dp轉移的時間複雜度是多少要考慮周全
代碼:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
const int MAXN = 1026;
int n, m;
int w[MAXN][MAXN], f[MAXN][MAXN];
int dp[MAXN][MAXN];
bool vis[MAXN];
void dfs(int x, int now) {
for (int i = 0; i <= ( 1 << now ); i++) dp[x][i] = 0;
if (!now) {
for (int i = 1; i <= n - 1; i++) {
if (vis[i])
dp[x][1] += w[x][i];
else
dp[x][0] += f[x][i];
}
return;
}
vis[now] = 0;
dfs(x * 2, now - 1);
dfs(x * 2 + 1, now - 1);
for (int j = 0; j <= ( 1 << now ); j++) {
for (int k = 0; k <= j; k++) dp[x][j] = max(dp[x][j], dp[x * 2][k] + dp[x * 2 + 1][j - k]);
}
vis[now] = 1;
dfs(x * 2, now - 1);
dfs(x * 2 + 1, now - 1);
for (int j = 0; j <= ( 1 << now ); j++) {
for (int k = 0; k <= j; k++) dp[x][j] = max(dp[x][j], dp[x * 2][k] + dp[x * 2 + 1][j - k]);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= (1 << (n - 1)); i++) {
for (int j = 1; j < n; j++) scanf("%d", &w[(1 << (n - 1)) + i - 1][j]);
}
for (int i = 1; i <= (1 << (n - 1)); i++) {
for (int j = 1; j < n; j++) scanf("%d", &f[(1 << (n - 1)) + i - 1][j]);
}
dfs(1, n - 1);
for (int i = 0; i <= m; i++) dp[1][1] = max(dp[1][1], dp[1][i]);
printf("%d\n", dp[1][1]);
}