思维+树形dp——gym101677A (难)

/*
拼接思想:
    g[u][j]表示u子树里距离u距离>=j的所有点都被覆盖的代价(子树里扣掉一个以u为圆心的扇形) 
        g[u][j]=sum{g[v][j-1]} 
    f[u][j]表示u子树内以及距离u距离<=j的所有点都被覆盖的代价 (子树加上以u为圆心的圆) 
        f[u][j]=min(f[v][j+1]-g[v][j],j)+g[u][j+1] 
    这两个数组不能独立求,因为g[u][0]是由f[u][0]决定 
*/
#include<bits/stdc++.h>
using namespace std;
#define N 5005

vector<int>G[N];
int n,f[N][N],g[N][N],d[N];

void dfs(int u,int pre){
    for(auto v:G[u])
        if(v!=pre)dfs(v,u);
    for(int i=1;i<=n;i++)
        for(auto v:G[u]){
            if(v==pre)continue;
            g[u][i]+=g[v][i-1];
        }
    f[u][n]=n;//给f[u]顶一个初始状态 
    for(int i=n-1;i>=0;i--){
        f[u][i]=max(1,i)+g[u][max(1,i)+1];//在u处放基站的代价 
        f[u][i]=min(f[u][i],f[u][i+1]);//和之前的比较 
        for(auto v:G[u]){
            if(v==pre)continue;
            f[u][i]=min(f[u][i],f[v][i+1]-g[v][i]+g[u][i+1]);
        } 
    } 
    
    g[u][0]=f[u][0];//g[u]真正的初始状态 
    for(int i=1;i<=n;i++)//贪心的扫一下g[u] 
        g[u][i]=min(g[u][i],g[u][i-1]);
}

int main(){
    cin>>n;
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u); 
    }
    
    dfs(1,1);
    
    cout<<f[1][0]<<'\n';
} 

 

posted on 2020-03-29 20:19  zsben  阅读(126)  评论(0编辑  收藏  举报

导航