此题有树,那么能用的求最值的算法有dp,贪心,流等等,如果我们以dp的思路来解题,观察每颗子树,那么每颗子树都相当于曼哈顿路径的一部分链,子树根可以在链的头,尾,中间,其中根在头和尾是等价的。那么可以设计一个状态dp[i][0]表示,以i为根的树组成的一个从根i开始的链条,要花费的最小代价,dp[i][1]表示以i为根的树组成的一个从非根节点开始的链条,注意两个状态表示的链的出点都是非根节点。那么如果i是叶子节点,dp[i][0]=dp[i][1]=0;
现在考虑转移:我们现在的目标是把各个子节点代表的链条,和根节点,一起串成一个链。
如果根节点选择的子节点的状态为dp[i][0]那么不需要新加任何边,直接用树边就好了,如果选择的是dp[i][1]那么新加一条边。
我们现在需要讨论的就是到底子节点是选择用dp[i][0]好,还是选择用dp[i][1]好,显然如果我们对于每个子节点都能用dp[i][0]与dp[i][1]中的最小值,而且不加任何边是最好的,但是由于用了最小值,那么有可能就会新加一些边,这样就不一定好了
转移的时候就是首先找出根节点的各个子节点的min(dp[子节点][0],dp[子节点][1])的和,再考虑如何将所有这些链条串成一个。我们尽量考虑多用树边,但是这会和我们前面求和矛盾,因为如果我选了dp[子节点][1]这个链条进行组合,是不能用树边的不是?那么我前面的操作都是无用功了?当然不是。注意到,如果dp[子节点][1]<dp[子节点][0]那么dp[子节点][1]<=dp[子节点][0]-1,也就是说,我们就算多加一条边,最后得到的结果也是小于等于选择dp[字节点][0]来做链条的.有了这个性质就很好转移了
minn表示最小值的和,cnt表示选择的dp[子节点][0]的个数,size表示总的子节点数。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int first[150200], nextt[300400], too[300400];
int edgetot;
void addedge(int a, int b)
{
too[edgetot] = b;
nextt[edgetot] = first[a];
first[a] = edgetot++;
too[edgetot] = a;
nextt[edgetot] = first[b];
first[b] = edgetot++;
}
int n;
int dp[150200][2];
void com(int i, int &minn, int &cnt)
{
if (dp[i][0] < dp[i][1])
{
minn += dp[i][0];
cnt++;
}
else
{
if (dp[i][0] == dp[i][1])
{
minn += dp[i][0];
cnt++;
}
else
minn += dp[i][1];
}
}
void dfs(int now, int fa)
{
int cnt, minn, size;
minn = 0; cnt = 0; size = 0;
for (int i = first[now]; i != -1; i = nextt[i])
{
int to = too[i];
if (to == fa)continue;
dfs(to, now);
com(to, minn, cnt);
size++;
}
if (size == 0)
{
dp[now][0] = dp[now][1] = 0;
return;
}
else
{
if (cnt >= 1)
{
dp[now][0] = min(dp[now][0], minn - 1+size);
}
else
{
dp[now][0] = min(dp[now][0], minn+size);
}
if (cnt >= 2)
{
dp[now][1] = min(dp[now][1], minn + size - 2);
}
else
{
if (cnt >= 1)
dp[now][1] = min(dp[now][1], minn + size - 1);
else
dp[now][1] = min(dp[now][1], minn + size);
}
}
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
first[i] = -1;
dp[i][0] = dp[i][1] = 1000000000;
}
for (int i = 0; i < n - 1; i++)
{
int a, b;
scanf("%d%d", &a, &b);
addedge(a, b);
}
dfs(1, 1);
printf("%d\n",min(dp[1][0] + 1,dp[1][1]+1));
return 0;
}