巡逻(论为什么第二次求直径要用dp)
题面
在一个地区有 n 个村庄,编号为1,2,…,n。
有 n-1 条道路连接着这些村庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其他任一个村庄。
每条道路的长度均为1个单位。
为保证该地区的安全,巡警车每天都要到所有的道路上巡逻。
警察局设在编号为1的村庄里,每天巡警车总是从警局出发,最终又回到警局。
为了减少总的巡逻距离,该地区准备在这些村庄之间建立 K 条新的道路,每条新道路可以连接任意两个村庄。
两条新道路可以在同一个村庄会合或结束,甚至新道路可以是一个环。
因为资金有限,所以 K 只能为1或2。
同时,为了不浪费资金,每天巡警车必须经过新建的道路正好一次。
编写一个程序,在给定村庄间道路信息和需要新建的道路数的情况下,计算出最佳的新建道路的方案,使得总的巡逻距离最小。
输入格式
第一行包含两个整数 n 和 K。
接下来 n-1 行每行两个整数 a 和 b,表示村庄 a 和 b 之间有一条道路。
输出格式
输出一个整数,表示新建了 K 条道路后能达到的最小巡逻距离。
数据范围
3≤n≤100000,
1≤K≤2,
1≤a,b≤n
输入样例:
8 1
1 2
3 1
3 4
5 3
7 5
8 5
5 6
输出样例:
11
题解
先说一下主要思路吧
k = 1/ 2, 说白了就是造两个环
第一个很简单, 就是连接直径
第二条也是连接直径(第二大), 那问题来了, 如何消除已经连过的直径?
将所连过的直径权值又1改-1, 即可
相当于你在算这条边未直径时, 贡献值为-1
那为什么不应该是贡献值为 0 呢?
其实是, 当你第一次连接直径时, 原本要原路返回, 这条边会走两次, 你连接之后直走一次
第二次连接时, 这条边会由于你的连线非但没少走, 还会多走一边故, 贡献从 1 变为 -1(新建的边必须走一次)
基本思路如此
问题来了, 第二次改变完边的权值时, 你不能用 dfs 或 bfs 去求直径, 为什么呢?
看一组数据(别人造的)
10 2
6 10
5 3
6 4
7 3
8 3
6 1
1 9
2 1
6 3
按照思路走一遍, 你会发现
第二次求直径时, 第一次搜索, 随着你选取的点不同, 找出的直径中的某个端点也不同
你拿着这个端点再去求, 另一个端点, 你求的直径能对吗?
所以第二次求直径要dp
那为什么第一次不直接也用dp求呢?
搜索可以回溯啊!改权值好改
代码
#include <bits/stdc++.h>
#define all(n) (n).begin(), (n).end()
#define se second
#define fi first
#define pb push_back
#define mp make_pair
#define sqr(n) (n)*(n)
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define per(i,a,b) for(int i=a;i>=(b);--i)
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
typedef vector<int> VI;
typedef double db;
const int N = 100005;
int n, m, _, k;
bool v[N];
int h[N], ne[N << 1], co[N << 1], to[N << 1], tot;
int d[N], f[N], b[N];
void add(int u, int v, int c) {
to[++tot] = v; ne[tot] = h[u]; h[u] = tot; co[tot] = c;
}
void dfs(int u) {
v[u] = 1;
for (int i = h[u]; i; i = ne[i]) {
int y = to[i];
if (v[y]) continue;
f[y] = u; b[y] = i; d[y] = d[u] + co[i];
if (d[0] < d[y])
d[0] = d[y], f[0] = y;
dfs(y);
}
}
void work(int& p, int& q) {
memset(v, 0, sizeof v); d[0] = -N;
d[1] = 0; dfs(1); p = f[0];
memset(v, 0, sizeof v); d[0] = -N;
d[p] = 0; dfs(p); q = f[0];
}
void dpfind(int u, int& ans) {
v[u] = 1;
for (int i = h[u]; i; i = ne[i]) {
int y = to[i];
if (v[y]) continue;
dpfind(y, ans);
ans = max(ans, d[u] + d[y] + co[i]);
d[u] = max(d[u], d[y] + co[i]);
}
d[u] = max(d[u], 0);
}
int main() {
ios::sync_with_stdio(0); cin.tie(0);
cin >> n >> k;
rep(i, 2, n) {
int u, v; cin >> u >> v;
add(u, v, 1); add(v, u, 1);
}
int p, q, ans = (n - 1) << 1;
work(p, q);
ans -= d[0] - 1;
if (k == 2) {
for (int i = q; i != p; i = f[i]) {
co[b[i]] = -1;
co[b[i] + ((b[i] & 1) ? 1 : -1)] = -1;
}
memset(d, 0xbf, sizeof d);
memset(v, 0, sizeof v);
int res = 0;
dpfind(p, res);
ans -= res - 1;
}
cout << ans;
return 0;
}