根据牛客的每日一题,学习了换根dp(没整好dp的我直接整上了换根dp),还是很不错的,学习了不会的知识,其实最难的地方应该也就是转移那里
换根dp大体思路:
- 用邻接表存储信息
- 然后dfs随机选取一点作为根,然后找出他的贡献值
- 另外dfs第一次搜索时,我们需要记录一些题目需要求出的信息(或者一些转换的信息)
- 然后我们第二次dfs时候,求出相关的转移数据,这里应该会另外开一个数组来记录
- 最终遍历这个数组,求出最大值或最小值
首先看下简单版本的第一题,只说解题思路
解题思路:
- 首先我们按照邻接表存储
- 然后我们首先dfs一遍(以1为根节点),这一遍我们存储每个节点有多少子节点,然后存储以1为根的情况下,每个节点的深度
- 然后我们开一个数组fa,存储当前节点1,题目中所要求的深度和。
- 我们再dfs一遍,这次我们要实现换根,这里的贡献值,如果由 u 换到 v, 那么贡献值就由fa[v] = fa[u] - dist[v] + (n - dist[v]) , 这里dist记录的节点数目。
- 最终我们求fa的最小值即可
这里借鉴雨巨的一个图,来更形象的表达一下换根的过程 相关链接
相关代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int N = 1000010, M = 2000010;
int dep[N], h[N], ne[M], e[M], dist[N], idx;
long long fa[N];
int n;
void add(int x, int y){
e[idx] = y, ne[idx] = h[x], h[x] = idx++;
}
void dfs(int u, int f){
dist[u] = 1;
dep[u] = dep[f] + 1;
for (int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if (j != f){
dfs(j,u);
dist[u] += dist[j];
}
}
}
void dfs2(int u, int f){
for (int i = h[u]; i != -1 ; i = ne[i]){
int j = e[i];
if (j != f){
fa[j] = fa[u] + n - (long long) 2 * dist[j];
dfs2(j,u);
}
}
}
int main(){
scanf("%d",&n);
memset(h,-1,sizeof h);
memset(dep,-1,sizeof dep);
int x, y;
for (int i = 0; i < n - 1; i++){
scanf("%d%d",&x,&y);
add(x,y), add(y,x);
}
dfs(1,0);
for (int i = 1; i <= n; i++){
fa[1] += dep[i];
}
dfs2(1,0);
long long mx = 1e9;
for (int i = 1; i <= n; i++){
mx = min(mx,fa[i]);
}
printf("%lld\n",mx);
return 0;
}
第二题题目链接
这题和上面的题类似,就是题意不太好懂,转移的方法也不太好懂,自己写写可能就明白了
解题思路:
- 首先和之前的一样,这里多了一个边权,所以所开一个数组w记录边权
- 然后我们第一次遍历记录的是当前根节点(1),到根节点的最大流量,还有其他节点的流量(其他节点的流量都是从他们的子节点到当前节点的,不与父节点互通)
- 然后我们开一个数组f记录当前根节点的流量,然后我们开始第二次dfs,我们换根也就是转换他的流量,我们的流量都是从子节点流向根节点,所以当出现v ,u时, u为根节点,那么他需要改变的为 u -> v ,这里的u首先要改变一下,他首先取值为 f[v] = main(w, f[u] - min(flow[u][v], w), 边的流量和边改变由f[u]点流量的改变,这两种情况,最后取最大值即可。
相关代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010, M = 400010;
int h[N], ne[M], e[M], w[M], d[M], f[M], deg[N], idx;
void add(int x, int y, int z){
e[idx] = y, ne[idx] = h[x], w[idx] = z, h[x] = idx++;
}
void dfs1(int u, int fa){
for (int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
int ww = w[i];
if (j == fa) continue;
dfs1(j, u);
if (deg[j] == 1) d[u] += ww;
else d[u] += min(ww, d[j]);
}
}
void dfs2(int u, int fa){
for (int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
int ww = w[i];
if (j == fa) continue ;
f[j] = d[j] + min(ww,f[u] - min(ww,d[j]));
dfs2(j, u);
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
int n;
scanf("%d",&n);
memset(d, 0, sizeof d);
memset(f, 0, sizeof f);
memset(h, -1, sizeof h);
memset(deg, 0, sizeof h);
idx = 0;
for (int i = 0; i < n - 1; i++){
int x, y, z;
scanf("%d%d%d",&x,&y,&z);
add(x, y, z), add(y, x, z);
deg[x] ++;
deg[y] ++;
}
int x = 1;
dfs1(x,0);
f[x] = d[x];
dfs2(x, 0);
int res = 0;
for (int i = 1; i <= n; i ++){
res = max(res, f[i]);
}
printf("%d\n",res);
}
}