P3629 [APIO2010]巡逻

神似之前芜湖集训的一道题,但是比那道题简单。这个题最多加两条路,所以直接分类讨论。k=1找完直径后直接可以算,k=2就把原来直径权值附为-1(负负得正)之后求直径就行了。

ps:有负边的直径不能用dfs,得dp

题干:

题目描述

在一个地区中有 n 个村庄,编号为 1, 2, ..., n。有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任一个村庄。每条道路的长度均为 1 个单位。 为保证该地区的安全,巡警车每天要到所有的道路上巡逻。警察局设在编号 为 1 的村庄里,每天巡警车总是从警察局出发,最终又回到警察局。 下图表示一个有 8 个村庄的地区,其中村庄用圆表示(其中村庄 1 用黑色的 圆表示),道路是连接这些圆的线段。为了遍历所有的道路,巡警车需要走的距 离为 14 个单位,每条道路都需要经过两次。

为了减少总的巡逻距离,该地区准备在这些村庄之间建立 K 条新的道路, 每条新道路可以连接任意两个村庄。两条新道路可以在同一个村庄会合或结束 (见下面的图例(c))。 一条新道路甚至可以是一个环,即,其两端连接到同一 个村庄。 由于资金有限,K 只能是 12。同时,为了不浪费资金,每天巡警车必须 经过新建的道路正好一次。 下图给出了一些建立新道路的例子:

在(a)中,新建了一条道路,总的距离是 11。在(b)中,新建了两条道路,总 的巡逻距离是 10。在(c)中,新建了两条道路,但由于巡警车要经过每条新道路 正好一次,总的距离变为了 15。 试编写一个程序,读取村庄间道路的信息和需要新建的道路数,计算出最佳 的新建道路的方案使得总的巡逻距离最小,并输出这个最小的巡逻距离。

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define duke(i,a,n) for(register int i = a;i <= n;++i)
#define lv(i,a,n) for(register int i = a;i >= n;--i)
#define clean(a) memset(a,0,sizeof(a))
const int INF = 1 << 30;
typedef long long ll;
typedef double db;
template <class T>
void read(T &x)
{
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x)
{
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}
const int N = 1e5 + 5;
struct node
{
    int l,r,nxt,w;
}a[N];
int n,k,dep[N];
int lst[N],len = -1;
void add(int x,int y)
{
    a[++len].l = x;
    a[len].r = y;
    a[len].nxt = lst[x];
    a[len].w = 1;
    lst[x] = len;
}
int f[N];
void dfs(int x,int fa)
{
    dep[x] = dep[fa] + 1;
    f[x] = fa;
    for(int k = lst[x];k != -1;k = a[k].nxt)
    {
        int y = a[k].r;
        if(y == fa)
        {
            continue;
        }
        dfs(y,x);
    }
}
int maxn = 0;
int mx[N],se[N];
void dp(int x,int fa)
{
    for(int k = lst[x];k != -1;k = a[k].nxt)
    {
        int y = a[k].r;
        if(y == fa) continue;
        dp(y,x);
        if(mx[y] + a[k].w >= mx[x])
        {
            se[x] = mx[x];
            mx[x] = mx[y] + a[k].w;
        }
        else if(mx[y] + a[k].w > se[x])
        {
            se[x] = mx[y] + a[k].w;
        }
    }
    maxn = max(maxn,mx[x] + se[x]);
}
int main()
{
    read(n);read(k);
    memset(lst,-1,sizeof(lst));
    duke(i,1,n - 1)
    {
        int x,y;
        read(x);read(y);
        add(x,y);
        add(y,x);
    }
    int root = 1;
    dfs(1,0);
    for(int i = 1;i <= n;i++)
    {
        if(dep[i] > dep[root])
        {
            root = i;
        }
    }
//    cout<<root<<endl;
    clean(f);
    dep[0] = -1;
    dfs(root,0);
    int end = 1;
    for(int i = 1;i <= n;i++)
    {
        if(dep[i] > dep[end])
        {
            end = i;
        }
    }
//    cout<<root<<" "<<end<<" "<<dep[end]<<endl;
    if(k == 1)
    {
        int ans = 2 * n;
        ans -= dep[end];
        ans --;
        printf("%d\n",ans);
        return 0;
    }
    for(int i = end;i;i = f[i])
    {
        for(int k = lst[i];k != -1;k = a[k].nxt)
        {
            int y = a[k].r;
            if(y == f[i])
            {
                a[k].w = -1;
                a[k ^ 1].w = -1;
                break;
            }
        }
    }
    dp(1,0);
//    cout<<maxn<<endl;
    printf("%d\n",2 * n - dep[end] - maxn);
    return 0;
}

 

posted @ 2019-02-24 21:59  DukeLv  阅读(145)  评论(0编辑  收藏  举报