【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的更多相关文章

  1. 洛谷 P3177 [HAOI2015]树上染色 树形DP

    洛谷 P3177 [HAOI2015]树上染色 树形DP 题目描述 有一棵点数为 \(n\) 的树,树边有边权.给你一个在 \(0 \sim n\)之内的正整数 \(k\) ,你要在这棵树中选择 \( ...

  2. bzoj 4033: [HAOI2015]树上染色 [树形DP]

    4033: [HAOI2015]树上染色 我写的可是\(O(n^2)\)的树形背包! 注意j倒着枚举,而k要正着枚举,因为k可能从0开始,会使用自己更新一次 #include <iostream ...

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

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

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

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

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

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

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

    可以去UOJ看出题人的题解. 这样的合并,每一个点对只在lca处被考虑到,复杂度$O(n^2)$ #include <map> #include <ctime> #includ ...

  7. 【BZOJ4033】【HAOI2015】树上染色 树形DP

    题目描述 给你一棵\(n\)个点的树,你要把其中\(k\)个点染成黑色,剩下\(n-k\)个点染成白色.要求黑点两两之间的距离加上白点两两之间距离的和最大.问你最大的和是多少. \(n\leq 200 ...

  8. 【HAOI2015】树上染色 - 树形 DP

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

  9. BZOJ_4033_[HAOI2015]树上染色_树形DP

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

随机推荐

  1. Servlet & Filter 执行原理

    一.Servlet的两个Map 当请求到达后,web容器是如何查找Servlet的呢?执行流程又是什么? 可能很多人和我一样,只知道在web,xml中配置拦截规则,然后反射+映射就完事了? 当Serv ...

  2. ThinkPHP角色控制时的错误

    1.Table 'think.think_user' doesn't exist  等的原因是因为'DB_PREFIX' => 'think_', // 数据库表前缀没有配置好,在使用角色控制时 ...

  3. 基于Maven的ssm(spring+springMvc+Mybatis)框架搭建

    前言 本demo是在idea下搭建的maven项目,数据库使用Mysql,jdk版本是1.8.0_171,ideal:2017.3.5 一.新建项目 1.file->new->porjec ...

  4. vue框架介绍

    vue框架介绍 一.vue 概念 vue 是一种开发用户界面的渐进式开发框架.渐进式指的是:你可以将vue作为一部分嵌入到web应用中,带来丰富的交互体验 二.vue特点及常见开发中的高级功能 1.解 ...

  5. Redis-GEO

    一. Redis的GEO特性 Redis3.2版本提供了GEO功能,支持存储地理位置信息用来实现诸如摇一摇,附近位置这类依赖于地理位置信息的功能.二. 命令2.1 增加地理位置信息 命令:geoadd ...

  6. HTML中被废弃的标签<b><u><i><s>

    <strong>代替<b>给文字加粗 <ins>代替<u>给文本添加下划线 <em>代替<i>将文本倾斜 <del> ...

  7. Nginx 教程 2:性能

    为了获得更好的学习效果,我们建议你在本机安装 Nginx 并且尝试进行实践. tcp_nodelay, tcp_nopush 和 sendfile tcp_nodelay 在 TCP 发展早期,工程师 ...

  8. jsp 引入js、css修改后有缓存不及时更新

    解决:(增加随机版本号) <link rel="stylesheet" type="text/css" href="css/1.css?v=&l ...

  9. C++单纯的指针传参的问题

    C++指针传参也是单纯的复制一份地址,如下代码: #define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace st ...

  10. php非法输入数据类型

    1.空白输入 2.超长输入(如大于256个字符) 3.特殊字符(如·!@#¥%……&*()—=|.:‘:<>;'"<>?.,) 4.控制字符(如\r\n等) ...