P3177 [HAOI2015] 树上染色 (背包树形dp)

P3177 [HAOI2015] 树上染色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

考虑树形dp:令dp[x][j]为以x为根的子树中有j个黑点。

子节点的合并出父节点,显然为背包树形dp。

则dp[x][j]=max(dp[x][j],dp[v][k],dp[x][j-k]+calc(x,v));

(1)计算:calc(x,v);换成边(x,v)对答案的贡献,是本题的精髓。

显然边(x,v)的贡献由黑与黑,白与白组成.即由(v子树的黑)与(除v子树外的黑),(v子树的白)与(除v子树外的白组成)

v子树的黑:k

除v子树外的黑=总黑-v子树的黑:m-k

v子树的白:size[v]-k

除v子树的白=除v子树的点-除v子树外的黑=(n-size[v])-(m-k)

则calc(x,v)=k*(m-k)+(size[v]-k)*(n-msize[v]+k)

dp[x][j]=max(dp[x][j],dp[v][k],dp[x][j-k]+k*(m-k)+(size[v]-k)*(n-msize[v]+k)*val[x][v];

假如本题就这样交上去,那么只会得到80分。

(2)因为还有一个东西:不合法状态转移到合法状态

 

 

 由于calc(x,v)是始终大于0的,所以合法状态可能由不合法的状态转移过来,让合法状态的具有它不应具有的大.

一开始我们把所有状态都设成不合法状态,即memset(dp,-1,sizeof(dp))

初始化有:dp[x][0]=0,dp[x][1]

最后就树形dp 的 for要能优化就优化,很重要

 Code:
复制代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mp make_pair
#define pb push_back   
#define popb pop_back  
#define fi first
#define se second
const int N=2e3+10;
//const int M=;
//const int inf=0x3f3f3f3f;     
//const ll INF=0x3ffffffffffff;
int T,n,m,size[N];
ll dp[N][N],val[N][N];
vector<int> g[N];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
void dfs(int x,int fa)
{
    dp[x][0]=dp[x][1]=0;
    size[x]=1;
    for(auto v:g[x])
    {
        if(v==fa) continue;
        dfs(v,x);
        size[x]+=size[v];
    }
    for(auto v:g[x])
    {
        if(v==fa) continue;
        for(int j=min(m,size[x]);j>=0;j--) 
        {
//            dp[x][j]=dp[v][0]+size[v]*(n-m-size[v])*val[x][v];
            for(int k=0;k<=min(j,size[v]);k++) 
            {
                if(dp[x][j-k]==-1) continue;
                dp[x][j]=max(dp[x][j],dp[x][j-k]+dp[v][k]+
                            ( (m-k)*k + (size[v]-k)*(n-m-(size[v]-k)) )*val[x][v] );             
            }            
        }
        // 树形dp的dp循坏能优化就优化,否则很容易TLE 
        // 本题还有特色的不合法情况 
        //  必需写成 j=min(m,size[x]) 和 k<=min(j,size[v]),如果是 j=m 和 k<=j,就会出错 
    }
}
int main()
{
//    freopen("","r",stdin);
//    freopen("","w",stdout);
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int u=read(),v=read();
        ll t;scanf("%lld",&t);
        g[u].pb(v);g[v].pb(u);
        val[v][u]=val[u][v]=t;
    }
    memset(dp,-1,sizeof(dp));
    dfs(1,0);
    printf("%lld",dp[1][m]);
    return 0;
}
复制代码

 

 

 

 

posted @   QAQ啥也不会  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示