仔细思考后会发现和51nod1677 treecnt有异曲同工之妙

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染黑就能获得最大收益。

题目分析

这题目第一眼看上去根本不像是背包题吧……倒像是个贪心或者奇妙结论题。

但是可以像treecut一样,将答案按照树上每一条边来统计贡献。

我们把一颗树沿某条边分开,看成这个样子。

那么显然若知道这条边左右两边黑白点各有多少个,就可以计算这个情况下的答案了。

也就是说,如果我们确定一条边来把树分开,那就可以依靠枚举来确定最优答案。

观察一下这个问题是具有最优子结构的,也就是说变成了一个树上背包的形式:左右两边黑白点个数的不同情况各有体积和价值,求最大价值。

我们定义$f[x][i]$表示以$x$为根的子树中,有$i$个黑点,这种情况的最大价值。

考虑如何转移。自然是要枚举上图中的$i$和$j$。然后就是背包式地转移:

 for (int j=mn; j>=; j--)
for (int l=; l<=upp&&l<=j; l++)
{
ll left = 1ll*l*(k-l)*w;
ll right = 1ll*(size[v]-l)*(n-size[v]-k+l)*w;
f[x][j] = std::max(f[x][j], f[v][l]+f[x][j-l]+left+right);
}

其中mn和upp表示枚举的上界。

这个把统计转化为各个边的贡献,然后转为背包做还是很妙的。

总结

一类难以通过树形结构直接转移的动态规划问题,可以考虑对于边将树划分为两个部分的子问题,再分别维护答案。

后记

隔了几天回来看又作为一个不会做这题的人表示讲的有一点不详细。

首先有一个很大的疑问是:讲是把答案拆开统计,但是到底怎么计算这个“贡献”?这个“贡献”又是什么东西?

没写过题的人看了代码肯定又有疑问:为什么统计一下点数再乘个边权就行了?

这里放张图,备忘一下

 #include<bits/stdc++.h>
typedef long long ll;
const int maxn = ;
const int maxm = ;
const ll INF = 112921504606846976ll; struct Edge
{
int y,val;
Edge(int a=, int b=):y(a),val(b) {}
}edges[maxm];
int n,k;
int size[maxn];
int edgeTot,nxt[maxm],head[maxn];
ll f[maxn][maxn]; int read()
{
char ch = getchar();
int num = ;
bool fl = ;
for (; !isdigit(ch); ch = getchar())
if (ch=='-') fl = ;
for (; isdigit(ch); ch = getchar())
num = (num<<)+(num<<)+ch-;
if (fl) num = -num;
return num;
}
void dfs(int x, int fa)
{
size[x] = ;
for (int i=head[x]; i!=-; i=nxt[i])
if (edges[i].y!=fa) dfs(edges[i].y, x), size[x] += size[edges[i].y];
}
void addedge(int u, int v, int w)
{
edges[++edgeTot] = Edge(v, w), nxt[edgeTot] = head[u], head[u] = edgeTot;
}
void dp(int x, int fa)
{
f[x][] = f[x][] = ;
if (size[x]==) return;
int mn = std::min(k, size[x]);
for (int i=head[x]; i!=-; i=nxt[i])
{
int v = edges[i].y, w=edges[i].val, upp = std::min(k, size[v]);
if (v!=fa){
dp(v, x);
for (int j=mn; j>=; j--)
for (int l=; l<=upp&&l<=j; l++)
{
ll left = 1ll*l*(k-l)*w;
ll right = 1ll*(size[v]-l)*(n-size[v]-k+l)*w;
f[x][j] = std::max(f[x][j], f[v][l]+f[x][j-l]+left+right);
}
}
}
}
int main()
{
memset(head, -, sizeof head);
n = read(), k = read();
if (k<< > n) k = n-k;
for (int i=; i<n; i++)
{
int u = read(), v = read(), w = read();
addedge(u, v, w);
addedge(v, u, w);
}
dfs(, );
for (int i=; i<=n; i++)
for (int j=; j<=size[i]; j++)
f[i][j] = -INF;
dp(, );
printf("%lld\n",f[][k]);
return ;
}

END

【树形背包】bzoj4033: [HAOI2015]树上染色的更多相关文章

  1. BZOJ4033 HAOI2015 树上染色 【树上背包】

    BZOJ4033 HAOI2015 树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白 ...

  2. [BZOJ4033][HAOI2015]树上染色(树形DP)

    4033: [HAOI2015]树上染色 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2437  Solved: 1034[Submit][Stat ...

  3. BZOJ4033: [HAOI2015]树上染色(树形DP)

    4033: [HAOI2015]树上染色 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 3461  Solved: 1473[Submit][Stat ...

  4. [bzoj4033][HAOI2015]树上染色_树形dp

    树上染色 bzoj-4033 HAOI-2015 题目大意:给定一棵n个点的树,让你在其中选出k个作为黑点,其余的是白点,收益为任意两个同色点之间距离的和.求最大收益. 注释:$1\le n\le 2 ...

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

    题目 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间 ...

  6. bzoj4033 [HAOI2015]树上染色——树形DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4033 树形DP,状态中加入 x 与父亲之间的边的贡献: 边权竟然是long long... ...

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

    bzoj4033,懒得复制,戳我戳我 Solution: 定义状态\(dp[i][j]\)表示\(i\)号节点为根节点的子树里面有\(j\)个黑色节点时最大的贡献值 然后我们要知道的就是子节点到根节点 ...

  8. 洛谷P3177||bzoj4033 [HAOI2015]树上染色

    洛谷P3177 bzoj4033 根本不会做... 上网查了题解,发现只要在状态定义的时候就考虑每一条边全局的贡献就好了? 考虑边的贡献和修改状态定义我都想到了,然而并不能想到要结合起来 ans[i] ...

  9. [BZOJ4033]:[HAOI2015]树上染色(树上DP)

    题目传送门 题目描述 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色.将所有点染色后,你会获得黑点两两之间的距离加 ...

随机推荐

  1. 调用Web API将文件上传到服务器的方法(.Net Core)

    最近遇到一个将Excel通过Web API存到服务器的问题,其中涉及到Excel的读取.调用API.Web  API怎么进行接收. 一. Excel的读取.调用API Excel读取以及调用API的代 ...

  2. hyperledger fabric 1.0.5 分布式部署 (四)

    chaincode 的开发 作者在hyperledger fabric 1.0.5 分布式部署 (三)中向读者介绍了如何开发fabric 的chaincode,那么实际上chaincode 还有其他的 ...

  3. Win10系统特别卡的一个原因

    我遇到的Win10特别卡的原因是它自带的一个杀毒软件 迈克菲(McAfee)导致的,在卸载之前电脑真的特别卡,打开一个窗口都卡,,卸载了之后瞬间感觉电脑飞起来了.... 当然还有很多原因会导致电脑卡, ...

  4. MDX之Case When用法

    with member [Measures].[终端销售数量总计] as sum(ytd([日期].[年月].CurrentMember),[Measures].[终端销售数量]) member [M ...

  5. springmvc整合elasticsearch

    网上大多时关于springboot整合的,也有spring的,但是 坑太多,都没法愉快的玩耍 这篇让我整合成功 https://www.cnblogs.com/sunny1009/articles/7 ...

  6. docker命令自动安装

    docker命令自动安装 操作 操作就是执行两句脚本 curl -fsSL get.docker.com -o get-docker.sh 这句命令会在当前文件夹下下载一个get-docker.sh的 ...

  7. 关于nodejs模块安装后找不到包解决办法

    主要原因是类似bower.gulp这些包后,没有添加到环境变量,但是有洁癖的我也不希望添加太多的软链接,所以在用phpstorm开始时有需要的情况下 定义临时的环境变量 http://stackove ...

  8. systemback-----做你折腾的后盾

    http://imcn.me/html/y2015/24421.html ubuntu的系统还原工具,最近在学习grunt,要安装nodejs 等一些依赖,对于有轻微系统洁癖的我来说是个好的解决方案, ...

  9. echarts 添加Loading 等待。

    capturedsDetailsEcharts: function(id) { if (!id) { id = mini.get("chnNameCaptureds").getVa ...

  10. JVM垃圾回收机制一

    JVM内存分配与回收 JVM 分代 JVM把堆分为年轻代和老年代,年轻代又分为1个Eden区和2个Survivor区,Eden和Survivor的内存的大小比例是8:1:1. 为什么要分代? 很大的原 ...