BZOJ4033 [HAOI2015]树上染色(洛谷P3177)

树形DP

BZOJ题目传送门
洛谷题目传送门

神题一点不会。。。

考虑每条边对答案的贡献。设 f [ x ] [ i ] 表示以 x 为根的子树中有 i 个黑点时对答案的贡献,则有 f [ x ] [ i ] = m a x { f [ j ] [ k ] + w } ,其中 j i 的儿子, w 表示 i j 这条边对答案的贡献,即子树内黑/白点的数量 子树外黑/白点的数量 这条边的长度。

注意循环的正反和范围,具体见代码。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 2005
using namespace std;
typedef long long LL;
struct edge{ int nxt,to; LL d; }ed[N<<1];
int n,m,k,h[N],fa[N],sz[N];
LL f[N][N];
#define addedge(x,y,z) ed[++k]=(edge){h[x],y,z},h[x]=k
void dfs(int x){
    sz[x]=1,f[x][0]=f[x][1]=0;
    for (int i=h[x],v;i;i=ed[i].nxt)
        if ((v=ed[i].to)!=fa[x]){
            fa[v]=x,dfs(v);
            for (int j=min(sz[x]+sz[v],m);~j;j--)
                //这里要倒序,或者另开一个数组存上一次的结果
                for (int p=max(0,j-sz[x]);p<=min(sz[v],j);p++){
                    //这里开始范围要取max,因为黑点个数不可能超过子树大小
                    LL w=(1ll*p*(m-p)+1ll*(sz[v]-p)*(n-m-sz[v]+p))*ed[i].d;
                    f[x][j]=max(f[x][j],f[x][j-p]+f[v][p]+w);
                }
            sz[x]+=sz[v];
        }
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1,x,y,z;i<n;i++){
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z),addedge(y,x,z);
    }
    for (int i=1;i<=n;i++)
        for (int j=0;j<=m;j++)
            f[i][j]=-1e9;//一定要给负
    return dfs(1),printf("%lld",f[1][m]),0;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/81035799