Problem - 1637F - Towers(树形DP+思维)

题目大意:给定一棵树,每个点有自己的权值 w i w_i wi,你需要在其中若干个节点上造塔,塔的高度 h i h_i hi就是造塔的花费,对于每个节点 i i i,必须保证存在两座塔, u , v u,v u,v,使得 w i ≤ m i n ( h u , h v ) w_i\le min(h_u,h_v) wimin(hu,hv),且 i i i位于 u , v u,v u,v的简单路径上.求造塔的最小费用和.

解题思路

这个题有一个贪心的思想,首先需要考虑权值最大点的情况,为了满足最大点一定符合,那么最小的花费 c o s t m i n ≥ 2 ∗ w m a x cost_{min}\ge 2*w_{max} costmin2wmax,一定是有两个权值为 w m a x w_{max} wmax的塔,那我们考虑怎么放这两座塔可以使得它对整棵树的贡献最大.

还需要考虑的一个点是,对于每个叶子节点,都需要造一座塔,因为只有这样,叶子节点才能保证被遍历.在这个基础之上,我们考虑把两座权值最大的塔都放在叶子节点上,然后把权值最大的点放在根,这样可以尽可能的让权值最大的塔对其他点的贡献最大,我们考虑写成树形DP的话,就是不断地去更新子树的最大值,如果当前向上的过程中出现了比子树更大的点,那我们就将最下面的叶子节点中权值最大的替换成当前这个点的权值,然后不断向上传递.

再考虑这两座权值最大的塔怎么放.

如果根节点有两个即以上的子树,那我们把所有子树的最大值都拿出来,然后去所有值中的最大和次大值,替换成 h m a x h_{max} hmax.

如果根节点只有一棵子树,那么一座塔放在子树上,另一座塔只能放在根本身的位置了.

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define syncfalse ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const int N = 2e5+5;
ll h[N];
vector<int>edge[N];
ll ans = 0;
ll maxh[N];
int root = 0;
ll dfs(int rt, int fa){
    maxh[rt]=h[rt];
    if (edge[rt].size()==1&&rt!=root){
        ans+=h[rt];
//        cout << rt << " " << ans << "\n";
        return maxh[rt];
    }
    ll now = 0;
    for (auto x : edge[rt]){
        if (x==fa)continue;
        now=max(now, dfs(x, rt));
    }
    if (h[rt]>now&&rt!=root){
        ans+=(h[rt]-now);
    }
//    cout << rt << " " << ans << "\n";
    maxh[rt]=max(maxh[rt], now);
    return maxh[rt];
}

int main(){
    syncfalse
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    int n;
    cin>>n;
    ll maxd = 0;
    for (int i = 1; i <= n; ++i)cin>>h[i],maxd=max(maxd,h[i]);
    for (int u, v, i = 1; i < n; ++i){
        cin>>u>>v;
        edge[u].push_back(v);
        edge[v].push_back(u);
    }
    for (int i = 1; i <= n; ++i){
        if (h[i]==maxd){
            root=i;break;
        }
    }
    dfs(root, 0);
    if (edge[root].size()==1){
        ans+=maxd;
        for (auto x : edge[root]){
            ans+=(maxd-maxh[x]);
        }
    }
    else{
        int maxd1 = 0, maxd2 = 0;
        for (auto x : edge[root]){
            if (maxh[x]>maxd1)maxd2=maxd1,maxd1=maxh[x];
            else if (maxh[x]>maxd2)maxd2=maxh[x];
        }
        ans+=2*maxd-maxd1-maxd2;
    }
    cout << ans << "\n";

    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值