巡逻(论为什么第二次求直径要用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;
}
posted @ 2020-06-25 19:17  洛绫璃  阅读(138)  评论(0编辑  收藏  举报