戰爭調度(樹形DP+BFS)

題目

題目描述

臉哥最近來到了一個神奇的王國,王國裏的公民每個公民有兩個下屬或者沒有下屬,這種關係剛好組成一個 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(n2^{n-1})好像可以接受,再來看轉移

由於樹形DP是要從下往上轉移,而我們枚舉了每個祖先的狀態,其實葉子結點x的dp[x][0]與dp[x][1]都已經出來了,那麼就可像其他dp一樣邊枚舉邊轉移

那麼式子就出來了:

dp[i][j] = max( dp[i*2][k] + dp[i*2+1][j-k] )

看一下重難點:

主要是要想到怎樣去枚舉狀態,對題目的條件分析要更全面一些

其實可根據輸入方式來進行分析

同時,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]);
}

 

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