战争调度(树形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]);
}

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页