发之前突然在前面加一句,大部分题解都很简单,其实没什么不好,点到点上就行了。只是对萌新来说有点痛苦,所以我写的都比较详细,希望可以帮助到萌新(想起了痛苦的往事),要是还可以的话点个赞呗。另如果发现错误烦请指正。
划重点:Treeisland is a country with n cities and n−1 two-way road and from any city you can go to any other cities.这句话说有个城市条双向道路,且从任意一个城市可以到达另一个城市,说明是一颗树。
问题:要把个点两两配对,使得路径之和最小,一定是偶数。
基本思路:每个点和就近的配对,问题是当就近的点不止一个时该怎么选择?如下图所示的树中点是选点还是选点亦或者更远的点呢?
结论: 如果某条边连接的子树中的结点个数是奇数则选择该边,是偶数则不选。
- 奇数以子树e为例,如果不选w2边的话,那么e子树中两两配对必定会剩下一个,剩下的那一个去和其他点配对的话路径长度会不小于w2。
- 偶数以子树a为例,如果选了w1边的话,同理a子树中无论怎么配对始终会剩下一个,而剩下的那一个去和其他点配对必定会经过点s,路径长度不小于w1。
- 如果有多个奇数结点的子树,都要选吗?怎么理解?还是以子树a为例,a有b、c、d三个奇数结点的子树,所以w3、w4、w5三条边都要选。但选了不代表a就一定要和b配对,a可以配对c,然后b和d配对。选了只是说在子树a的最优解决方案中会有w3这条边。
- 以此推到任意子树的解,选择某条边并不是说根结点一定和该边对应的结点配对,而是说在这颗子树的最优解中有这条边,而不关心具体的配对方案。
严谨:是结点而不是节点!!!敲了半天节点感觉怪怪的,幸好百度了一下。
#include <bits/stdc++.h> using namespace std; #define LL long long const int INF = 0x3f3f3f3f; ///1 061 109 567 const int negative_infinite = 0xcfcfcfcf; ///-808 464 433 const double pi = acos(-1); const int mod = 1e9 + 7; const double eps = 1e-8; const int MAXN = 1e5 + 117; const int MAXM = 2e5 + 117; int t, n; int u, v; LL w, sum; bool vis[MAXN]; struct Edge { int v, ne; LL w; } edge[MAXN]; int head[MAXN], tot; void init() { sum = tot = 0; memset(vis, false, sizeof(vis)); memset(head, -1, sizeof(head)); } void addedge(int u, int v, LL w) { edge[tot].v = v; edge[tot].w = w; edge[tot].ne = head[u]; head[u] = tot++; } int dfs(int id) { vis[id] = true; int ret = 1, num; for(int i = head[id]; i != -1; i = edge[i].ne) { if(vis[edge[i].v]) continue; num = dfs(edge[i].v); if(num & 1) sum += edge[i].w; ret += num; } return ret; } int main() { scanf("%d", &t); while(t--) { init(); scanf("%d", &n); for(int i = 1; i < n; i++) { scanf("%d%d%lld", &u, &v, &w); addedge(u, v, w); addedge(v, u, w); } dfs(1); printf("%lld\n", sum); } return 0; }