bzoj 4033 树上染色 - 树形动态规划

  有一棵点数为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染黑就能获得最大收益。


  动态规划的第一步——设计状态,f[i][j]表示以i节点为根的子树中染了j个黑点的"收益"。

  不过这样没有黑点的位置,这么多个点,总不可能用N进制来表示点的位置。所以只能换个思路。

  对于当前考虑的这棵子树,我知道染了j个节点,那么我知道在这棵子树内的白点数和子树外的白点数和黑点数。因此我可以计算出节点i到它的父节点的那条边的对答案的贡献,对于子节点转移到父节点就是一个用dp合并的过程,因此解决了状态转移的问题,时间复杂度为O(nk)。

  注意dp时不合法的状态一定不能转移(看代码吧,或者自己想想也可以,状态转移前有个if)

Code

  1 /**
  2  * bzoj
  3  * Problem#4033
  4  * Accepted
  5  * Time:630ms
  6  * Memory:17092k
  7  */
  8 #include<iostream>
  9 #include<fstream>
 10 #include<sstream>
 11 #include<algorithm>
 12 #include<cstdio>
 13 #include<cstring>
 14 #include<cstdlib>
 15 #include<cctype>
 16 #include<cmath>
 17 #include<ctime>
 18 #include<map>
 19 #include<stack>
 20 #include<set>
 21 #include<queue>
 22 #include<vector>
 23 #ifndef WIN32
 24 #define AUTO "%lld"
 25 #else
 26 #define AUTO "%I64d"
 27 #endif
 28 using namespace std;
 29 typedef bool boolean;
 30 #define inf 0xfffffff
 31 #define smin(a, b) (a) = min((a), (b))
 32 #define smax(a, b) (a) = max((a), (b))
 33 template<typename T>
 34 inline boolean readInteger(T& u) {
 35     char x;
 36     int aFlag = 1;
 37     while(!isdigit((x = getchar())) && x != ‘-‘ && x != -1);
 38     if(x == -1)    {
 39         ungetc(x, stdin);
 40         return false;
 41     }
 42     if(x == ‘-‘) {
 43         aFlag = -1;
 44         x = getchar();
 45     }
 46     for(u = x - ‘0‘; isdigit((x = getchar())); u = u * 10 + x - ‘0‘);
 47     u *= aFlag;
 48     ungetc(x, stdin);
 49     return true;
 50 }
 51
 52 ///map template starts
 53 typedef class Edge{
 54     public:
 55         int end;
 56         int next;
 57         int w;
 58         Edge(const int end = 0, const int next = 0, const int w = 0):end(end), next(next), w(w){}
 59 }Edge;
 60
 61 typedef class MapManager{
 62     public:
 63         int ce;
 64         int *h;
 65         Edge *edge;
 66         MapManager(){}
 67         MapManager(int points, int limit):ce(0){
 68             h = new int[(const int)(points + 1)];
 69             edge = new Edge[(const int)(limit + 1)];
 70             memset(h, 0, sizeof(int) * (points + 1));
 71         }
 72         inline void addEdge(int from, int end, int w){
 73             edge[++ce] = Edge(end, h[from], w);
 74             h[from] = ce;
 75         }
 76         inline void addDoubleEdge(int from, int end, int w){
 77             addEdge(from, end, w);
 78             addEdge(end, from, w);
 79         }
 80         Edge& operator [] (int pos) {
 81             return edge[pos];
 82         }
 83 }MapManager;
 84 #define m_begin(g, i) (g).h[(i)]
 85 ///map template ends
 86
 87 template<typename T>class Matrix{
 88     public:
 89         T *p;
 90         int lines;
 91         int rows;
 92         Matrix():p(NULL){    }
 93         Matrix(int rows, int lines):lines(lines), rows(rows){
 94             p = new T[(lines * rows)];
 95         }
 96         T* operator [](int pos){
 97             return (p + pos * lines);
 98         }
 99 };
100 #define matset(m, i, s) memset((m).p, (i), (s) * (m).lines * (m).rows)
101
102 int n, k;
103 MapManager g;
104 Matrix<long long> f;
105 int* size;
106
107 inline void init() {
108     readInteger(n);
109     readInteger(k);
110     g = MapManager(n, 2 * n);
111     f = Matrix<long long>(n + 1, k + 1);
112     size = new int[(const int)(n + 1)];
113     matset(f, 0, sizeof(long long));
114     for(int i = 1, a, b, c; i < n; i++) {
115         readInteger(a);
116         readInteger(b);
117         readInteger(c);
118         g.addDoubleEdge(a, b, c);
119     }
120 }
121
122 void treedp(int node, int fa, int len) {
123     size[node] = 1;
124     for(int i = m_begin(g, node); i != 0; i = g[i].next) {
125         int& e = g[i].end;
126         if(e == fa)     continue;
127         treedp(e, node, g[i].w);
128         size[node] += size[e];
129         for(int j = min(size[node], k); j >= 0; j--) {
130             for(int s = 0; s <= size[e] && s <= j; s++) {
131                 if(j - s <= size[node] - size[e])
132                     smax(f[node][j], f[node][j - s] + f[e][s]);
133             }
134         }
135     }
136     for(int i = 0; i <= min(size[node], k); i++)
137             f[node][i] += (i * 1LL * (k - i) + (size[node] - i) * 1LL * (n - k - size[node] + i)) * len;
138 }
139
140 inline void solve() {
141     treedp(1, 0, 0);
142     printf(AUTO"\n", f[1][k]);
143 }
144
145 int main() {
146     init();
147     solve();
148     return 0;
149 }
时间: 2024-10-03 13:38:54

bzoj 4033 树上染色 - 树形动态规划的相关文章

【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]树上染色(树形dp)

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

BZOJ 4033 HAOI2015 T1 树形DP

题目大意:给定一棵树,你需要把其中的k个点染成黑色,使得黑色点两两之间的距离和+白色点两两之间的距离和最大,求最大值 题解戳这里 Orz ydcydc 看来我对于非线性的树形DP还是做得太少了QwQ #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 2020 using namespace std; struct edge{ int

【题解】 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

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]树上染色 树上背包

[题目]#2124. 「HAOI2015」树上染色 [题意]给定n个点的带边权树,要求将k个点染成黑色,使得 [ 黑点的两两距离和+白点的两两距离和 ] 最大.n<=2000. [算法]树上背包 [题解]设f[i][j]表示子树i中有j个黑点对答案的贡献(包括点 i 到父亲的边 p ),由于边p的贡献只和 j 有关,所以最后再统计. 所以做树上背包即可,注意这题特殊在f[x][0]≠0,所以初始f[x][k]+=f[y][0],然后不要把0作为物品. 最后统计边p的贡献:w[p] *(子树内黑点

[BZOJ 4033] [HAOI2015] T1 【树形DP】

题目链接:BZOJ - 4033 题目分析 使用树形DP,用 f[i][j] 表示在以 i 为根的子树,有 j 个黑点的最大权值. 这个权值指的是,这个子树内部的点对间距离的贡献,以及 i 和 Father[i] 之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数 * 子树外部的黑点数 * 这条边的权值). 然后DFS来求,枚举 i 的每个儿子 j,现在的 f[i][] 是包含了 [1, j-1] 子树,然后两重循环枚举范围是 [1, j - 1] 的子树总 Size 和

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

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

Facebook Hacker Cup 2015 Round 1--Corporate Gifting(树形动态规划)

原题:https://www.facebook.com/hackercup/problems.php?pid=759650454070547&round=344496159068801 题意:给定一颗有根树,在树上下层的节点要给上层节点礼物,根节点的礼物则给慈善会,但是给礼物有个条件就是你不能送你的父节点已经送出的礼物.问满足要求的最少花费. 题解:这个题卡了一段时间,类似于染色问题,可以用树形动态规划求解.因为已知节点个数为N,则我们单个节点的最大花费不会超过log2(N) = 18. 1.