网址:https://www.luogu.com.cn/problem/P3629ios
在一个地区中有\(n\)个村庄,编号为\(1, 2, ..., n\)。有\(n–1\)条道路链接着这些村庄,每条道路恰好链接两个村庄,从任何一个村庄,均可以经过这些道路到达其 他任一个村庄。每条道路的长度均为\(1\)个单位。 为保证该地区的安全,巡警车天天要到全部的道路上巡逻。警察局设在编号为\(1\)的村庄里,天天巡警车老是从警察局出发,最终又回到警察局。 下图表示一个有\(8\)个村庄的地区,其中村庄用圆表示(其中村庄\(1\)用黑色的圆表示),道路是链接这些圆的线段。为了遍历全部的道路,巡警车须要走的距 离为\(14\)个单位,每条道路都须要通过两次。算法
为了减小总的巡逻距离,该地区准备在这些村庄之间创建\(K\)条新的道路, 每条新道路能够链接任意两个村庄。两条新道路能够在同一个村庄会合或结束 (见下面的图例(c))。 一条新道路甚至能够是一个环,即,其两端链接到同一 个村庄。 因为资金有限,\(K\)只能是\(1\)或\(2\)。同时,为了避免浪费资金,天天巡警车必须 通过新建的道路正好一次。 下图给出了一些创建新道路的例子:安全
在(a)中,新建了一条道路,总的距离是\(11\)。在(b)中,新建了两条道路,总 的巡逻距离是 \(10\)。在(c)中,新建了两条道路,但因为巡警车要通过每条新道路 正好一次,总的距离变为了\(15\)。 试编写一个程序,读取村庄间道路的信息和须要新建的道路数,计算出最佳 的新建道路的方案使得总的巡逻距离最小,并输出这个最小的巡逻距离。spa
第一行包含两个整数\(n,K(1≤K≤2)\)。接下来\(n–1\) 行,每行两个整数\(a,b\), 表示村庄\(a\)与\(b\)之间有一条道路\((1≤a,b≤n)\)。code
输出一个整数,表示新建了\(K\)条道路后能达到的最小巡逻距离。blog
8 1 1 2 3 1 3 4 5 3 7 5 8 5 5 6
11
8 2 1 2 3 1 3 4 5 3 7 5 8 5 5 6
10
5 2 1 2 2 3 3 4 4 5
6
\(10%\)的数据中,\(n≤1000,K=1\);get
\(30%\)的数据中,\(K=1\);string
\(80%\)的数据中,每一个村庄相邻的村庄数不超过\(25\);io
\(90%\)的数据中,每一个村庄相邻的村庄数不超过\(150\);class
\(100%\)的数据中,\(3≤n≤100,000, 1≤K≤2\)。
咱们先不考虑加边的状况。若是正常从\(1\)号结点走通过全部的边后返回,那么至少要走的路径长度为多少?很显然 \((n-1)*2\)。咱们能够考虑边\((u,v)\),当从\(u\)遍历至\(v\)的时候,因为每一个节点到根节点只有惟一一条简单路径,所以要返回的时候必须通过该边。每条边最少通过两次。
咱们新建道路有这样的原则:必定要成环,且只容许通过一次。
#include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<cmath> #include<queue> using namespace std; const int SIZE = 100000 + 5; vector <int> G[SIZE]; bool vis[SIZE] = {}, chosen[SIZE];//vis 用于BFS求树的直径, chosen 记录该节点是否在树的直径 int n, k, prev[SIZE], dis[SIZE], dp[SIZE];//prev 记录结点的前驱 int d1 = 0, d2 = 0; void bfs()//第一遍:找直径 { queue <int> Q; while(!Q.empty()) Q.pop(); memset(dis, 0, sizeof(dis)); Q.push(1); int u, v, s, t; while(!Q.empty()) { u = Q.front(); Q.pop(); for(int i = 0; i < G[u].size(); ++ i) { int v = G[u][i]; if(!vis[v]) { Q.push(v); vis[v] = true; } } } memset(vis, false, sizeof(vis)); s = u; Q.push(s); while(!Q.empty()) { u = Q.front(); Q.pop(); for(int i = 0; i < G[u].size(); ++ i) { v = G[u][i]; if(vis[v]) continue; vis[v] = true; Q.push(v); dis[v] = dis[u] + 1; prev[v] = u; } } memset(chosen, false, sizeof(chosen)); t = u; d1 = dis[t]; chosen[s] = true; do { chosen[u] = true; u = prev[u]; } while(u != s); return; } void dfs(int u, int Fa) { for(int i = 0; i < G[u].size(); ++ i) { int v = G[u][i], op; if(v == Fa) continue; if(chosen[u] && chosen[v]) op = -1; else op = 1; dfs(v, u); d2 = max(d2, dp[u] + dp[v] + op); dp[u] = max(dp[u], dp[v] + op); } return; } int main() { scanf("%d %d", &n, &k); for(int i = 0; i < n; ++ i) G[i].clear(); int x, y; for(int i = 1; i < n; ++ i) { scanf("%d %d", &x, &y); G[x].push_back(y), G[y].push_back(x); } bfs(); if(k > 1) { memset(dp, 0, sizeof(dp)); dfs(1, 0); printf("%d\n", (n << 1) - d1 - d2); } else printf("%d\n", (n << 1) - d1 - 1); return 0; }
将边权更改为为咱们但愿的数值,这是值得借鉴模仿的地方。