HDU Tree(树形dp)

http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1007&cid=883

题意:

给一个树,边权。有一个整数k,找树的子图G,满足:G内连通、度大于k的顶点个数不大于1、总权值要尽可能大。

解析:

对于每个点,维护 d o w n [ i ] down[i] down[i]表示往下选择 k − 1 k-1 k1个儿子(儿子也最多选择 k − 1 k-1 k1个)的最大权值。

接下来利用 d o w n down down维护出 u p up up数组,表示当前点为中心(可以大于K)时,上方所能得到的最大权值。

u p up up数组从上往下维护,对于一个点,假设儿子 u u u为中心,那么 u u u这条边一点要选了。其它的,可以在父亲或其它儿子中选出 K − 1 K-1 K1个点。

做法是将所有 d o w n [ s o n ] + ∣ e d g e ∣ down[son]+|edge| down[son]+edge u p [ f a ] up[fa] up[fa]排序后选 K − 1 K-1 K1个最大的,然后遍历每个儿子,判断如果选择了这个儿子则删去再加一个权值次大的。

代码:

/*
 *  Author : Jk_Chen
 *    Date : 2020-08-04-13.10.05
 */
#include
using namespace std;
#define LL long long
#define LD long double
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<"> "<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=2e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/

#define rep_e(i,p,u) for(int i=head[p],u=to[i];i;i=nex[i],u=to[i])
int head[maxn],to[maxn<<1],nex[maxn<<1],val[maxn<<1],now;
void add(int a,int b,int v){
    nex[++now]=head[a];head[a]=now;to[now]=b;val[now]=v;
}
/*_________________________________________________________edge*/

bool cmp(pill a,pill b){
    return a.fi>b.fi;
}

int n,k;
bool vis[maxn];
LL up[maxn];
LL down[maxn];
LL ans[maxn];
pill tmp[maxn];
void dfs(int p,int fa){
    rep_e(i,p,u){
        if(u==fa)continue;
        dfs(u,p);
    }
    int son=0;
    rep_e(i,p,u){
        if(u==fa)continue;
        tmp[++son]={down[u]+val[i],u};
    }
    sort(tmp+1,tmp+1+son,cmp);
    down[p]=0;
    rep(i,1,min(son,k-2)){
        down[p]+=tmp[i].fi;
    }
}

void dfs2(int p,int fa){/// up[i]:点i为中心时,可以从上得到的权值
    ans[p]=up[p];
    rep_e(i,p,u){
        if(u==fa)continue;
        ans[p]+=down[u]+val[i];
    }
    /// deal up
    if(k==2){
        rep_e(i,p,u){
            if(u==fa)continue;
            up[u]=val[i];
            dfs2(u,p);
        }
        return;
    }

    int son=0;
    rep_e(i,p,u){
        if(u==fa)continue;
        vis[u]=0;
        tmp[++son]={down[u]+val[i],u};
    }
    if(fa!=0){
        tmp[++son]={up[p],fa};
    }
    sort(tmp+1,tmp+1+son,cmp);

    LL S=0;
    int Nex;
    rep(i,1,k-2){
        if(i>son)break;
        S+=tmp[i].fi;
        vis[tmp[i].se]=1;
        Nex=i+1;
    }
    rep_e(i,p,u){
        if(u==fa)continue;
        if(vis[u]){
            up[u]=S-(down[u]+val[i]);
            if(Nex<=son)
                up[u]+=tmp[Nex].fi;
        }
        else{
            up[u]=S;
        }
        up[u]+=val[i];
    }
    rep_e(i,p,u){
        if(u==fa)continue;
        dfs2(u,p);
    }
}

int t=rd;
int main(){
    while(t--){
        now=0;
        n=rd;
        k=rd+1;
        rep(i,1,n){
            head[i]=0;
        }
        rep(i,1,n-1){
            int a=rd,b=rd,v=rd;
            add(a,b,v);
            add(b,a,v);
        }
        if(k<2){
            puts("0");
            continue;
        }
        dfs(1,0);
        dfs2(1,0);
        LL Ans=0;
        rep(i,1,n){
            Ans=max(Ans,ans[i]);
        }
        printf("%lld\n",Ans);
    }
    return 0;
}

你可能感兴趣的:(DP动态规划)