P3629 [APIO2010]巡逻

题目大意:

给你一颗树,要把所有点遍历一次后回到原点。为了减少移动次数,你可以自己加入K条边,然后尽可能减少要走的道路树。K只为1,或者2

思路:

很显然我们要对K进行分类讨论,K=1时,要使得走过的路最少,显然是找到树的直径。
那么对于K=2的时候呢,我们仔细思考一下就会发现,对于两条路重叠的部分,因为每个点一定要走,所以要多走一次。那么我们把直径上的边权值取反,再找出一条权值最大的路径,减掉就好了。不能用bfs求树的直径,要用dp

程序:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#define N 1000000
using namespace std;
int p,cmax,cnt,n,k,ans,o,x,s;
int dep[N],f[N],last[N],d[N],v[N];
queue<int>q;
struct data{int to,next,w,fr;}e[N];
void add(int x,int y,int w){
    e[++cnt].to=y; e[cnt].fr=x;e[cnt].w=w; e[cnt].next=last[x]; last[x]=cnt;
    e[++cnt].to=x; e[cnt].fr=y;e[cnt].w=w; e[cnt].next=last[y]; last[y]=cnt;
}

void bfs(int x){
    while (!q.empty()) q.pop();
    q.push(x);
    cmax=0;
    memset(dep,30,sizeof(dep));
    dep[x]=1;
    while (!q.empty()){
        int u=q.front();
        q.pop();
        for (int i=last[u];i;i=e[i].next)
        if (dep[e[i].to]==505290270){
            dep[e[i].to]=dep[u]+e[i].w;
            f[e[i].to]=i;
            if (dep[e[i].to]>cmax){
                cmax=dep[e[i].to];
                s=e[i].to;
                p=i;
            }
            q.push(e[i].to);
        }
    }
}


void dp(int x){
    v[x]=1;
    for (int i=last[x];i;i=e[i].next){
        int y=e[i].to;
        if (v[y]) continue;
        dp(y);
        o=max(o,d[x]+d[y]+e[i].w);
        d[x]=max(d[x],d[y]+e[i].w);
    }
}

int main(){
    scanf("%d%d",&n,&k);
    cnt=1;
    for (int i=1;i<=n-1;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y,1);
    }
    ans=(n-1)*2+k;
    bfs(1);
    o=p;

    bfs(s);
    while (1==1){
        ans-=e[p].w;
        e[p^1].w=-e[p^1].w;
        e[p].w=-e[p].w;
        if (p==(o^1)) break;
        p=f[e[p].fr];
    }
    if (k==2){
        o=0;
        dp(1);
        ans-=o;
    }
    printf("%d",ans);
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq872425710/article/details/79959420
文章标签: 何嘉阳
个人分类: 树形dp bfs
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭