版权声明:希望能在自己成长的道路上帮到更多的人,欢迎各位评论交流 https://blog.csdn.net/yiqzq/article/details/82119837
原题地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5568
题意:给出一个棵树,每条边权都为1,要求在点1和点x之间连一条边,使得连边之后,点1到所有点的路径长度之和最小。 输出最小长度之和。
思路:我们可以先进行一遍dfs求出每个节点的子节点数,然后,再用于一个dfs进行状态祝转移.
状态转移解释具体看代码
#include <bits/stdc++.h>
#define eps 1e-8
#define INF 0x3f3f3f3f
#define PI acos(-1)
#define lson l,mid,rt<<1
#define rson mid+1,r,(rt<<1)+1
#define CLR(x,y) memset((x),y,sizeof(x))
#define fuck(x) cerr << #x << "=" << x << endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int seed = 131;
const int maxn = 2e5 + 5;
const int mod = 1e9 + 7;
int t, n;
struct node {
int v, nxt;
} e[maxn * 2];
int head[maxn], tot, num[maxn];
ll MIN, sum;
int dep[maxn];//dp[i]=j,表示在当前链上深度为i的点是j
ll dp[maxn];//dp[i]=j,表示从根节点向i连一条边的最小总花费
void init_head() {
for (int i = 0; i <= n; i++) {
head[i] = -1;
num[i] = 0;
}
MIN = 1e18;
sum = 0;
tot = 0;
}
void add_edge(int u, int v) {
e[tot].v = v;
e[tot].nxt = head[u];
head[u] = tot++;
}
int dfs(int u, int d, int fa) {
num[u] = 1;
sum += d - 1;
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa) continue;
num[u] += dfs(v, d + 1, u);
}
return num[u];
}
void dfs2(int u, int d, int fa) {
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa) continue;
dep[d + 1] = v;
dp[v] = dp[u] - 2 * num[v] + num[dep[(d + 2) / 2 + 1]];
/*
解释一下转移方程
因为dp的转移是从u节点到v节点,所以画个图比划一下就知道,由于是从u节点到v节点
那么使得以v为根节点的子节点的权值都会-1
所以 dp[u]-num[v]
然后下面减了,上面相对应的就会比多,
而多的是以链的中点为根节点的子节点减去以v为根节点的子节点数
这就是为什么减去的是2倍的num[v]
最后由于求的是v节点,而v节点的深度是d+1,然后这里的(d + 2) / 2 + 1就是中点,
具体的可以自己画图研究
*/
dfs2(v, d + 1, u);
}
}
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
init_head();
for (int i = 2; i <= n; i++) {
int u, v;
scanf("%d%d", &u, &v);
add_edge(u, v);
add_edge(v, u);
}
dfs(1, 1, -1); //深度从1开始方便计算
dp[1] = sum;
dep[1] = 1;
for (int i = head[1]; ~i; i = e[i].nxt) {
/*
因为要想使总和减少,那么选择与根节点直接相连的没用,
所以从根节点的子节点的子节点开始遍历dp
这里有一个很巧妙的方法,我们换一种定义dep深度数组,这样子
我们就可以直接求中点查询中点的子节点数
*/
int v = e[i].v;
dp[v] = sum;
dep[2] = v;
dfs2(v, 2, 1);
}
for (int i = 1; i <= n; i++) {
MIN = min(MIN, dp[i]);
}
printf("%lld\n", MIN);
}
return 0;
}