洛谷P3262 [JLOI2015]战争调度

探讨在一棵n层的完全二叉树上,如何通过合理染色使叶子节点及其祖先节点的贡献值最大化,同时确保黑色叶子节点数量不超过m。采用树形背包问题的思路,通过递归枚举非叶子节点颜色并合并左右子树贡献值,实现O(2^{2n}

Description

给你一棵 n 层的完全二叉树,每个节点可以染黑白两种颜色。对于每个叶子节点及其某个祖先节点,如果它们均为黑色则有一个贡献值,如果均为白色则有另一个贡献值。要求黑色的叶子节点数目不超过 m ,求最大总贡献值。


Input

第一行两个数 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 ,依次往上。


Output

一行一个数表示满足条件的最大贡献值


Solution

[NOI2006]网络收费 的套路。

提前计算叶子节点的总贡献,设 f[i][j] 表示以 i 为根的子树中,j 个叶子节点的颜色为黑色( j 个平民选择战争)的最大总贡献值。

那么这显然是一个树形背包问题,处理左右节点后背包合并即可。

但是由于叶子节点的贡献与其祖先节点的颜色选择有关,我们不能直接得到贡献。由于这是一棵完全二叉树,因此可以暴力枚举每个非叶子节点的颜色。

这样有递归式 T(1)=O(logk),T(k)=4T(k/2)+O(k2)T(1)=O(logk),T(k)=4T(k/2)+O(k^2)T(1)=O(logk),T(k)=4T(k/2)+O(k2),不考虑 T(1) 时根据主定理解得 T(k)=O(k2logk)T(k)=O(k^2logk)T(k)=O(k2logk),考虑 T(1)时 T(1)被计算了k2k^2k2 次,贡献为 O(k2logk)O(k^2logk)O(k2logk)

因此总的时间复杂度是正确的,为 O(22n⋅n)O(2^{2n}⋅n)O(22nn)


Code

#include <bits/stdc++.h>
using namespace std;
int f[1030][1030];
int w[1030][11];
int v[1030][11];
int n,m,ans;
int vis[11];
void dfs(int x,int d){
    for(int i=0;i<=1<<d;i++) 
	f[x][i]=0;
    if(!d){
        for(int i=1;i<=n;i++){
        	if(vis[i]) f[x][1]+=w[x][i];
            else f[x][0]+=v[x][i];
		} 
        return;
    }
    vis[d]=0;
    dfs(x<<1,d-1);
    dfs(x<<1|1,d-1);
    for(int i=0;i<=1<<(d-1);i++)
    for(int j=0;j<=1<<(d-1);j++)
    f[x][i+j]=max(f[x][i+j],f[x<<1][i]+f[x<<1|1][j]);
    vis[d]=1;
    dfs(x<<1,d-1);
    dfs(x<<1|1,d-1);
    for(int i=0;i<=1<<(d-1);i++)
    for(int j=0;j<=1<<(d-1);j++)
    f[x][i+j]=max(f[x][i+j],f[x<<1][i]+f[x<<1|1][j]);
}
int main(){
    scanf("%d%d",&n,&m); n--;
    for(int i=0;i<(1<<n);i++) 
    for(int j=1;j<=n;j++) 
    scanf("%d",&w[i+(1<<n)][j]);
    for(int i=0;i<(1<<n);i++) 
    for(int j=1;j<=n;j++) 
    scanf("%d",&v[i+(1<<n)][j]);
    dfs(1,n);
    for(int i=0;i<=m;i++) 
    ans=max(ans,f[1][i]);
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值