【HAOI2015】树上染色—树形dp
【HAOI2015】树上染色
【题目描述】
有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间距离的和的收益。问收益最大值是多少。
【输入格式】
第一行两个整数N,K。
接下来N-1行每行三个正整数fr,to,dis,表示该树中存在一条长度为dis的边(fr,to)。输入保证所有点之间是联通的。
【输出格式】
输出一个正整数,表示收益的最大值。
【输入样例1】
3 1
1 2 1
1 3 2
【输出样例1】
3
【输入样例2】
5 2
1 2 3
1 5 1
2 3 1
2 4 2
【输出样例2】
17
【样例解释】
在第二个样例中,将点1,2染黑就能获得最大收益。
【数据范围】
对于30%的数据,N<=20
对于50%的数据,N<=100
对于100%的数据,N<=2000,0<=K<=N。
题解:
这题一看就是dp,单个点无法产生贡献,只能是两个黑点或两个白点才能产生贡献
如果我们dp围绕点来进行,首先你无法定义一种状态,其次你不方便转移
所以我们考虑一条边能产生的贡献
首先一条边能产生的贡献为:
Wi=dis×((这条边左边的黑点×这条边右边的黑点)+(这条边左边的白点×这条边右边的白点))
我们考虑一对点产生的贡献,这一条边L左侧的黑点要想连接L右侧的黑点,必定经过L,所以L会有贡献
有几对这样的黑点,边L就被经过几次,白点同理,所以得到了上面的方程
我们设f[i][j]表示以i为根节点的子树中染j个黑点的最大收益,
size[x]表示以x为根的子树大小,目标:f[1][k];
设当前搜索到以x为根的子树,设son为x的一个儿子,则:
$f[x][j]=max(f[x][p]+f[son][j-p]+Wi),$
$j<=min(size[x],k),p<=min(size[son],k)$
Wi表示x与son所连边产生的贡献,在上面已经解释过了
更新f的时候,如果正着推,就需要把f存到一个临时数组里;循环完后再赋值给f,如果倒着枚举直接更新就好了
当然你还可以减少枚举的数量,即j<=min(size[x],k),p<=min(size[son],k)。
下面给出两份代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MAXN 2005
using namespace std;
ll n,k;
ll fr[MAXN<<],to[MAXN<<],nxt[MAXN<<],pre[MAXN],cnt=,dis[MAXN<<];
void add(ll u,ll v,ll w){
cnt++,fr[cnt]=u,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt,dis[cnt]=w;
}
ll f[MAXN][MAXN],temp[MAXN],size[MAXN];
void dfs(ll x,ll fa){
size[x]=;
for(ll i=pre[x];i;i=nxt[i]){
ll y=to[i];
if(y==fa) continue;
dfs(y,x);
ll num_b1=min(k,size[x]),num_b2=min(k,size[y]),l;
memset(temp,,sizeof(temp));
for(ll j=;j<=num_b1;j++){
for(ll p=;p<=num_b2&&p+j<=k;p++){
l=dis[i]*((k-p)*p+(size[y]-p)*(n-k-size[y]+p));
temp[j+p]=max(temp[j+p],f[x][j]+f[y][p]+l);
}
}
ll m=min(num_b1+num_b2,k);
for(ll j=;j<=m;j++)
f[x][j]=temp[j];
size[x]+=size[y];
}
}
int main(){
scanf("%lld%lld",&n,&k);
for(ll i=,u,v,d;i<n;i++){
scanf("%lld%lld%lld",&u,&v,&d);
add(u,v,d),add(v,u,d);
}
dfs(,);
printf("%lld\n",f[][k]);
return ;
}
正序
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define MAXN 2005
using namespace std;
ll n, k;
ll to[MAXN << ], nxt[MAXN << ], pre[MAXN], cnt = , dis[MAXN << ];
void add(ll u, ll v, ll w) { cnt++, to[cnt] = v, nxt[cnt] = pre[u], pre[u] = cnt, dis[cnt] = w; }
ll f[MAXN][MAXN], temp[MAXN][MAXN], size[MAXN];
void dfs(ll x, ll fa) {
size[x] = ;
for (ll i = pre[x]; i; i = nxt[i]) {
ll y = to[i];
if (y == fa)
continue;
dfs(y, x);
ll num_b1 = min(k, size[x]), l;
for (ll j = num_b1; j >= ; j--) {
ll num_b2 = min(k - j, size[y]);
for (ll p = num_b2; p >= ; p--) {
l = dis[i] * ((k - p) * p + (size[y] - p) * (n - k - size[y] + p));
f[x][j + p] = max(f[x][j + p], f[x][j] + f[y][p] + l);
}
}
size[x] += size[y];
}
}
int main() {
scanf("%lld%lld", &n, &k);
for (ll i = , u, v, d; i < n; i++) {
scanf("%lld%lld%lld", &u, &v, &d);
add(u, v, d), add(v, u, d);
}
dfs(, );
printf("%lld\n", f[][k]);
return ;
}
倒序,感谢loj码风优化
【HAOI2015】树上染色—树形dp的更多相关文章
- 洛谷 P3177 [HAOI2015]树上染色 树形DP
洛谷 P3177 [HAOI2015]树上染色 树形DP 题目描述 有一棵点数为 \(n\) 的树,树边有边权.给你一个在 \(0 \sim n\)之内的正整数 \(k\) ,你要在这棵树中选择 \( ...
- bzoj 4033: [HAOI2015]树上染色 [树形DP]
4033: [HAOI2015]树上染色 我写的可是\(O(n^2)\)的树形背包! 注意j倒着枚举,而k要正着枚举,因为k可能从0开始,会使用自己更新一次 #include <iostream ...
- 【BZOJ4033】[HAOI2015]树上染色 树形DP
[BZOJ4033][HAOI2015]树上染色 Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染 ...
- [BZOJ4033][HAOI2015]树上染色(树形DP)
4033: [HAOI2015]树上染色 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2437 Solved: 1034[Submit][Stat ...
- bzoj4033 [HAOI2015]树上染色——树形DP
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4033 树形DP,状态中加入 x 与父亲之间的边的贡献: 边权竟然是long long... ...
- BZOJ 4033 [HAOI2015]树上染色 ——树形DP
可以去UOJ看出题人的题解. 这样的合并,每一个点对只在lca处被考虑到,复杂度$O(n^2)$ #include <map> #include <ctime> #includ ...
- 【BZOJ4033】【HAOI2015】树上染色 树形DP
题目描述 给你一棵\(n\)个点的树,你要把其中\(k\)个点染成黑色,剩下\(n-k\)个点染成白色.要求黑点两两之间的距离加上白点两两之间距离的和最大.问你最大的和是多少. \(n\leq 200 ...
- 【HAOI2015】树上染色 - 树形 DP
题目描述 有一棵点数为 N 的树,树边有边权.给你一个在 0~ N 之内的正整数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的N-K个点染成白色 . 将所有点染色后,你会获得黑点两两之 ...
- BZOJ_4033_[HAOI2015]树上染色_树形DP
BZOJ_4033_[HAOI2015]树上染色_树形DP Description 有一棵点数为N的树,树边有边权.给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并 将其他的 ...
随机推荐
- Servlet & Filter 执行原理
一.Servlet的两个Map 当请求到达后,web容器是如何查找Servlet的呢?执行流程又是什么? 可能很多人和我一样,只知道在web,xml中配置拦截规则,然后反射+映射就完事了? 当Serv ...
- ThinkPHP角色控制时的错误
1.Table 'think.think_user' doesn't exist 等的原因是因为'DB_PREFIX' => 'think_', // 数据库表前缀没有配置好,在使用角色控制时 ...
- 基于Maven的ssm(spring+springMvc+Mybatis)框架搭建
前言 本demo是在idea下搭建的maven项目,数据库使用Mysql,jdk版本是1.8.0_171,ideal:2017.3.5 一.新建项目 1.file->new->porjec ...
- vue框架介绍
vue框架介绍 一.vue 概念 vue 是一种开发用户界面的渐进式开发框架.渐进式指的是:你可以将vue作为一部分嵌入到web应用中,带来丰富的交互体验 二.vue特点及常见开发中的高级功能 1.解 ...
- Redis-GEO
一. Redis的GEO特性 Redis3.2版本提供了GEO功能,支持存储地理位置信息用来实现诸如摇一摇,附近位置这类依赖于地理位置信息的功能.二. 命令2.1 增加地理位置信息 命令:geoadd ...
- HTML中被废弃的标签<b><u><i><s>
<strong>代替<b>给文字加粗 <ins>代替<u>给文本添加下划线 <em>代替<i>将文本倾斜 <del> ...
- Nginx 教程 2:性能
为了获得更好的学习效果,我们建议你在本机安装 Nginx 并且尝试进行实践. tcp_nodelay, tcp_nopush 和 sendfile tcp_nodelay 在 TCP 发展早期,工程师 ...
- jsp 引入js、css修改后有缓存不及时更新
解决:(增加随机版本号) <link rel="stylesheet" type="text/css" href="css/1.css?v=&l ...
- C++单纯的指针传参的问题
C++指针传参也是单纯的复制一份地址,如下代码: #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace st ...
- php非法输入数据类型
1.空白输入 2.超长输入(如大于256个字符) 3.特殊字符(如·!@#¥%……&*()—=|.:‘:<>;'"<>?.,) 4.控制字符(如\r\n等) ...