bzoj4033: [HAOI2015]树上染色(树形dp)

4033: [HAOI2015]树上染色

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 3269  Solved: 1413
[Submit][Status][Discuss]

Description

有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并

将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。

问收益最大值是多少。

Input

第一行两个整数N,K。

接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。

输入保证所有点之间是联通的。

N<=2000,0<=K<=N

Output

输出一个正整数,表示收益的最大值。

Sample Input

5 2
1 2 3
1 5 1
2 3 1
2 4 2

Sample Output

17
【样例解释】
将点1,2染黑就能获得最大收益。

HINT

2017.9.12新加数据一组 By GXZlegend

Source

鸣谢bhiaibogf提供

#include<iostream>
#include<cstdio>
#include<cstring>

#define N 2001

using namespace std;
int n,m,k,ans,cnt;
int dis[N][N],e[N][N];
int p[N];

void calc_dis()
{
    for(int k=1;k<=n;k++) for(int i=1;i<=n;i++)
    {
      for(int j=1;j<=n;j++)
          if(i!=j && j!=k)
            e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
    }
}

void calc_ans()
{
    int tmp=0;
    for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++)
    if(p[i]==p[j]) tmp+=e[i][j];
    ans=max(ans,tmp);
}

void dfs(int now,int c)
{
    if(now==n+1)
    {
        if(c==k)calc_ans();
        return;
    }
    p[now]=0;dfs(now+1,c+1);
    p[now]=1;dfs(now+1,c);
}

int main()
{
    int x,y,z;
    scanf("%d%d",&n,&k);
    memset(e,127/3,sizeof e);
    for(int i=1;i<=n;i++) e[i][i]=0;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        e[x][y]=e[y][x]=z;
    }
    calc_dis();
    dfs(1,0);
    printf("%d\n",ans);
    return 0;
}

30暴力

/*
开始状态设的是f[i][j][0/1]。
表示i为根的子树染j个黑点,i节点为黑/白的最大值。
发现当i与儿子异色时没法转移,需要知道子树内点的具体染色情况。
所以说这个状态貌似不大行。

网上说“像这种题目其实是一个套路。在树上这种“两两之间”计算贡献和的问题,都拆开看每条边的贡献。”
我就很伤心,那不就说明我被套路了吗...

改一下状态f[i][j]表示i为根染j个黑点对总答案的最大贡献。
这个贡献的定义是啥呢?就是子树内所有边的贡献。
对于这条边
子树下有j个黑点,那么在其他位置就有k - j个黑点,所以由于黑点这条边被走过j*(k-j)次。
子树下有size[x] - j个白点,其他位置有n - k - (size[x]-j)个白点
所以由于白点这条边被走过(size[x]-j)*(n-k-size[x]+j)次。

*/
#include<iostream>
#include<cstdio>
#include<cstring>

#define N 2001
#define ll long long

using namespace std;
int n,k,ans,cnt,S,T;
int head[N],siz[N];
ll f[N][N],tmp;
struct edge{
    int u,v,net;
    ll w;
}e[N<<1];

inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c>‘9‘||c<‘0‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}

inline void add(int u,int v,ll w)
{
    e[++cnt].v=v;e[cnt].w=w;e[cnt].net=head[u];head[u]=cnt;
}

ll calc(ll val,int num,int x)//计算这条边的贡献
{
    val=val*x*(k-x)+val*(num-x)*(n-k-(num-x));
    return val;
}

void dfs(int u)
{
    siz[u]=1;
    for(int i=head[u];i;i=e[i].net)
    {
        int v=e[i].v;
        if(siz[v]) continue;
        dfs(v);
        for(int x=siz[u];x>=0;x--) for(int y=siz[v];y>=0;y--)//类似树上背包,枚举当前子树染黑个数
        {
            tmp=f[u][x]+f[v][y]+calc(e[i].w,siz[v],y);
            //f[u][x]其他子树内的点各自独立于当前子树内的点的贡献
            //f[v][y]当前子树内的点各自独立于其他子树内的点的贡献
            f[u][x+y]=max(f[u][x+y],tmp);
        }
        siz[u]+=siz[v];
    }
}

int main()
{
    int x,y,z;
    n=read();k=read();
    for(int i=1;i<n;i++)
    {
        x=read();y=read();cin>>z;
        add(x,y,z);add(y,x,z);
    }
    dfs(1);printf("%lld\n",f[1][k]);
    return 0;
}

原文地址:https://www.cnblogs.com/L-Memory/p/9768069.html

时间: 2024-10-10 03:45:14

bzoj4033: [HAOI2015]树上染色(树形dp)的相关文章

【BZOJ4033】[HAOI2015]树上染色 树形DP

[BZOJ4033][HAOI2015]树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益.问收益最大值是多少. Input 第一行两个整数N,K. 接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to). 输入保证所有点之间是联通的. N<=2000,0<=K&l

[BZOJ4033][HAOI2015]树上染色

试题描述 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益. 问收益最大值是多少. 输入 第一行两个整数N,K. 接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to). 输入保证所有点之间是联通的. N<=2000,0<=K<=N 输出 输出一个正整数,表示收益的最大值. 输入示例 5

BZOJ4033 [HAOI2015]树上染色 【树形dp】

题目 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益. 问收益最大值是多少. 输入格式 第一行两个整数N,K. 接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to). 输入保证所有点之间是联通的. N<=2000,0<=K<=N 输出格式 输出一个正整数,表示收益的最大值. 输入样例

【树形背包】bzoj4033: [HAOI2015]树上染色

仔细思考后会发现和51nod1677 treecnt有异曲同工之妙 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益. 问收益最大值是多少. Input 第一行两个整数N,K. 接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to). 输入保证所有点之间是联通的. N<=2

【题解】 bzoj4033: [HAOI2015]树上染色* (动态规划)

bzoj4033,懒得复制,戳我戳我 Solution: 定义状态\(dp[i][j]\)表示\(i\)号节点为根节点的子树里面有\(j\)个黑色节点时最大的贡献值 然后我们要知道的就是子节点到根节点这条边会计算次数就是:子树中白色节点数\(*\)子树外白色节点数\(+\)子树中黑色节点数\(*\)子树外黑色节点数 \[dp[u][j+k]=max(dp[u][j+k],\] \[dp[u][j]+dp[v][k]+(1ll)*k*(m-k)*dis[v]+(1ll)*(siz[v]-k)*(n

[BZOJ4033][HAOI2015]T1(树形dp)

题目描述 传送门 题解 f[i][j]表示以i为根的子树中选了j个黑点的最大获益. 考虑由子树转移到根,连接子树和根的路径的贡献为子树中所有黑(白)点和子树外所有黑(白)点的配对个数乘以边权. 代码 #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define LL long long const int max_n=2005; const int max_e=max

[HAOI2015]树上染色(树形dp)

[HAOI2015]树上染色 题目描述 有一棵点数为 N 的树,树边有边权.给你一个在 0~ N 之内的正整数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的N-K个点染成白色 . 将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益.问受益最大值是多少. 输入输出格式 输入格式: 第一行包含两个整数 N, K .接下来 N-1 行每行三个正整数 fr, to, dis , 表示该树中存在一条长度为 dis 的边 (fr, to) .输入保证所有点之间是联通的

bzoj 4033: [HAOI2015]树上染色

4033: [HAOI2015]树上染色 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 1886  Solved: 805 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益. 问收益最大值是多少. Input 第一行两个整数N,K. 接下来N-1行每行三个正整数fr

bzoj 4033: [HAOI2015]T1(树形DP)

4033: [HAOI2015]T1 Time Limit: 10 Sec  Memory Limit: 256 MB Submit: 819  Solved: 375 [Submit][Status][Discuss] Description 有一棵点数为 N 的树,树边有边权.给你一个在 0~ N 之内的正整 数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的 N-K个点染成白色 . 将所有点染色后,你会获得黑点两两之间的距 离加上白点两两之间的距离的和的受益.问受益最大值是多